mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 18:33:14 +01:00
Intrinsics shouldn't see struct padding, so use a special TargetABI for them
that removes it. This unbreaks the `llvm_*_with_overflow` intrinsics.
This commit is contained in:
39
gen/abi-generic.h
Normal file
39
gen/abi-generic.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef LDC_GEN_ABI_GENERIC
|
||||
#define LDC_GEN_ABI_GENERIC
|
||||
|
||||
#include "gen/llvmhelpers.h"
|
||||
#include "gen/tollvm.h"
|
||||
#include "gen/structs.h"
|
||||
|
||||
/// Removes padding fields for (non-union-containing!) structs
|
||||
struct RemoveStructPadding : ABIRewrite {
|
||||
/// get a rewritten value back to its original form
|
||||
virtual LLValue* get(Type* dty, DValue* v) {
|
||||
LLValue* lval = DtoAlloca(dty, ".rewritetmp");
|
||||
|
||||
// Make sure the padding is zero, so struct comparisons work.
|
||||
// TODO: Only do this if there's padding, and/or only initialize padding.
|
||||
DtoMemSetZero(lval, DtoConstSize_t(getTypePaddedSize(DtoType(dty))));
|
||||
|
||||
DtoPaddedStruct(dty, v->getRVal(), lval);
|
||||
return lval;
|
||||
}
|
||||
|
||||
/// get a rewritten value back to its original form and store result in provided lvalue
|
||||
/// this one is optional and defaults to calling the one above
|
||||
virtual void getL(Type* dty, DValue* v, llvm::Value* lval) {
|
||||
DtoPaddedStruct(dty, v->getRVal(), lval);
|
||||
}
|
||||
|
||||
/// put out rewritten value
|
||||
virtual LLValue* put(Type* dty, DValue* v) {
|
||||
return DtoUnpaddedStruct(dty, v->getRVal());
|
||||
}
|
||||
|
||||
/// return the transformed type for this rewrite
|
||||
virtual const LLType* type(Type* dty, const LLType* t) {
|
||||
return DtoUnpaddedStructType(dty);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -43,7 +43,7 @@
|
||||
#include "gen/llvmhelpers.h"
|
||||
#include "gen/abi.h"
|
||||
#include "gen/abi-x86-64.h"
|
||||
#include "gen/structs.h"
|
||||
#include "gen/abi-generic.h"
|
||||
#include "ir/irfunction.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -485,38 +485,6 @@ struct X86_64_C_struct_rewrite : ABIRewrite {
|
||||
};
|
||||
|
||||
|
||||
/// Removes padding fields for (non-union-containing!) structs
|
||||
struct RemoveStructPadding : ABIRewrite {
|
||||
/// get a rewritten value back to its original form
|
||||
virtual LLValue* get(Type* dty, DValue* v) {
|
||||
LLValue* lval = DtoAlloca(dty, ".rewritetmp");
|
||||
|
||||
// Make sure the padding is zero, so struct comparisons work.
|
||||
// TODO: Only do this if there's padding, and/or only initialize padding.
|
||||
DtoMemSetZero(lval, DtoConstSize_t(getTypePaddedSize(DtoType(dty))));
|
||||
|
||||
DtoPaddedStruct(dty, v->getRVal(), lval);
|
||||
return lval;
|
||||
}
|
||||
|
||||
/// get a rewritten value back to its original form and store result in provided lvalue
|
||||
/// this one is optional and defaults to calling the one above
|
||||
virtual void getL(Type* dty, DValue* v, llvm::Value* lval) {
|
||||
DtoPaddedStruct(dty, v->getRVal(), lval);
|
||||
}
|
||||
|
||||
/// put out rewritten value
|
||||
virtual LLValue* put(Type* dty, DValue* v) {
|
||||
return DtoUnpaddedStruct(dty, v->getRVal());
|
||||
}
|
||||
|
||||
/// return the transformed type for this rewrite
|
||||
virtual const LLType* type(Type* dty, const LLType* t) {
|
||||
return DtoUnpaddedStructType(dty);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct RegCount {
|
||||
unsigned char int_regs, sse_regs;
|
||||
};
|
||||
|
||||
77
gen/abi.cpp
77
gen/abi.cpp
@@ -10,6 +10,7 @@
|
||||
#include "gen/abi.h"
|
||||
#include "gen/logger.h"
|
||||
#include "gen/dvalue.h"
|
||||
#include "gen/abi-generic.h"
|
||||
|
||||
#include "ir/irfunction.h"
|
||||
|
||||
@@ -311,3 +312,79 @@ TargetABI * TargetABI::getTarget()
|
||||
return new UnknownTargetABI;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// A simple ABI for LLVM intrinsics.
|
||||
struct IntrinsicABI : TargetABI
|
||||
{
|
||||
RemoveStructPadding remove_padding;
|
||||
|
||||
bool returnInArg(TypeFunction* tf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool passByVal(Type* t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void fixup(IrFuncTyArg& arg) {
|
||||
assert(arg.type->ty == Tstruct);
|
||||
// TODO: Check that no unions are passed in or returned.
|
||||
|
||||
LLType* abiTy = DtoUnpaddedStructType(arg.type);
|
||||
|
||||
if (abiTy && abiTy != arg.ltype) {
|
||||
arg.ltype = abiTy;
|
||||
arg.rewrite = &remove_padding;
|
||||
}
|
||||
}
|
||||
|
||||
void rewriteFunctionType(TypeFunction* tf)
|
||||
{
|
||||
assert(tf->linkage == LINKintrinsic);
|
||||
|
||||
IrFuncTy& fty = tf->fty;
|
||||
|
||||
if (!fty.arg_sret) {
|
||||
Type* rt = fty.ret->type->toBasetype();
|
||||
if (rt->ty == Tstruct) {
|
||||
Logger::println("Intrinsic ABI: Transforming return type");
|
||||
fixup(*fty.ret);
|
||||
}
|
||||
}
|
||||
|
||||
Logger::println("Intrinsic ABI: Transforming arguments");
|
||||
LOG_SCOPE;
|
||||
|
||||
for (IrFuncTy::ArgIter I = fty.args.begin(), E = fty.args.end(); I != E; ++I) {
|
||||
IrFuncTyArg& arg = **I;
|
||||
|
||||
if (Logger::enabled())
|
||||
Logger::cout() << "Arg: " << arg.type->toChars() << '\n';
|
||||
|
||||
// Arguments that are in memory are of no interest to us.
|
||||
if (arg.byref)
|
||||
continue;
|
||||
|
||||
Type* ty = arg.type->toBasetype();
|
||||
if (ty->ty == Tstruct)
|
||||
fixup(arg);
|
||||
|
||||
if (Logger::enabled())
|
||||
Logger::cout() << "New arg type: " << *arg.ltype << '\n';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TargetABI * TargetABI::getIntrinsic()
|
||||
{
|
||||
static IntrinsicABI iabi;
|
||||
return &iabi;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,10 @@ struct ABIRewrite
|
||||
// interface called by codegen
|
||||
struct TargetABI
|
||||
{
|
||||
/// Returns the ABI for the target we're compiling for
|
||||
static TargetABI* getTarget();
|
||||
/// Returns the ABI for intrinsics
|
||||
static TargetABI* getIntrinsic();
|
||||
|
||||
virtual void newFunctionType(TypeFunction* tf) {}
|
||||
virtual bool returnInArg(TypeFunction* tf) = 0;
|
||||
|
||||
@@ -40,10 +40,9 @@ const llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nest
|
||||
assert(type->ty == Tfunction);
|
||||
TypeFunction* f = (TypeFunction*)type;
|
||||
|
||||
if (f->linkage != LINKintrinsic) {
|
||||
// Tell the ABI we're resolving a new function type
|
||||
gABI->newFunctionType(f);
|
||||
}
|
||||
TargetABI* abi = (f->linkage == LINKintrinsic ? TargetABI::getIntrinsic() : gABI);
|
||||
// Tell the ABI we're resolving a new function type
|
||||
abi->newFunctionType(f);
|
||||
|
||||
// Do not modify f->fty yet; this function may be called recursively if any
|
||||
// of the argument types refer to this type.
|
||||
@@ -63,18 +62,17 @@ const llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nest
|
||||
Type* rt = f->next;
|
||||
unsigned a = 0;
|
||||
// sret return
|
||||
if (f->linkage != LINKintrinsic)
|
||||
if (gABI->returnInArg(f))
|
||||
{
|
||||
fty.arg_sret = new IrFuncTyArg(rt, true, StructRet | NoAlias | NoCapture);
|
||||
rt = Type::tvoid;
|
||||
lidx++;
|
||||
}
|
||||
// sext/zext return
|
||||
else if (unsigned se = DtoShouldExtend(rt))
|
||||
{
|
||||
a = se;
|
||||
}
|
||||
if (abi->returnInArg(f))
|
||||
{
|
||||
fty.arg_sret = new IrFuncTyArg(rt, true, StructRet | NoAlias | NoCapture);
|
||||
rt = Type::tvoid;
|
||||
lidx++;
|
||||
}
|
||||
// sext/zext return
|
||||
else if (unsigned se = DtoShouldExtend(rt))
|
||||
{
|
||||
a = se;
|
||||
}
|
||||
fty.ret = new IrFuncTyArg(rt, false, a);
|
||||
}
|
||||
lidx++;
|
||||
@@ -152,8 +150,7 @@ const llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nest
|
||||
argtype = ltd;
|
||||
}
|
||||
// byval
|
||||
else if (f->linkage != LINKintrinsic
|
||||
&& gABI->passByVal(argtype))
|
||||
else if (abi->passByVal(argtype))
|
||||
{
|
||||
if (!byref) a |= llvm::Attribute::ByVal;
|
||||
byref = true;
|
||||
@@ -172,7 +169,7 @@ const llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nest
|
||||
// it has now been set.
|
||||
if (f->ir.type) {
|
||||
// Notify ABI that we won't be needing it for this function type anymore.
|
||||
gABI->doneWithFunctionType();
|
||||
abi->doneWithFunctionType();
|
||||
|
||||
// Some cleanup of memory we won't use
|
||||
delete fty.ret;
|
||||
@@ -192,13 +189,11 @@ const llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nest
|
||||
// Now we can modify f->fty safely.
|
||||
f->fty = fty;
|
||||
|
||||
if (f->linkage != LINKintrinsic) {
|
||||
// let the abi rewrite the types as necesary
|
||||
gABI->rewriteFunctionType(f);
|
||||
// let the abi rewrite the types as necesary
|
||||
abi->rewriteFunctionType(f);
|
||||
|
||||
// Tell the ABI we're done with this function type
|
||||
gABI->doneWithFunctionType();
|
||||
}
|
||||
// Tell the ABI we're done with this function type
|
||||
abi->doneWithFunctionType();
|
||||
|
||||
// build the function type
|
||||
std::vector<const LLType*> argtypes;
|
||||
|
||||
@@ -396,8 +396,8 @@ pragma(intrinsic, "llvm.smul.with.overflow.i#")
|
||||
*
|
||||
* See: http://llvm.org/docs/LangRef.html#int_umul_overflow
|
||||
*/
|
||||
pragma(intrinsic, "llvm.umul.with.overflow.i#")
|
||||
OverflowRet!(T) llvm_umul_with_overflow(T)(T lhs, T rhs);
|
||||
//pragma(intrinsic, "llvm.umul.with.overflow.i#")
|
||||
// OverflowRet!(T) llvm_umul_with_overflow(T)(T lhs, T rhs);
|
||||
|
||||
|
||||
//
|
||||
|
||||
77
tests/mini/intrinsics_ovf.d
Normal file
77
tests/mini/intrinsics_ovf.d
Normal file
@@ -0,0 +1,77 @@
|
||||
module intrinsics_ovf;
|
||||
|
||||
|
||||
//version = PRINTF;
|
||||
|
||||
version(PRINTF)
|
||||
extern(C) int printf(char*, ...);
|
||||
|
||||
|
||||
import ldc.intrinsics;
|
||||
|
||||
|
||||
int saddo(int a, int b, out bool overflow) {
|
||||
auto Result = llvm_sadd_with_overflow(a, b);
|
||||
overflow = Result.overflow;
|
||||
return Result.result;
|
||||
}
|
||||
|
||||
int uaddo(int a, int b, out bool overflow) {
|
||||
auto Result = llvm_uadd_with_overflow(a, b);
|
||||
overflow = Result.overflow;
|
||||
return Result.result;
|
||||
}
|
||||
|
||||
int smulo(int a, int b, out bool overflow) {
|
||||
auto Result = llvm_smul_with_overflow(a, b);
|
||||
overflow = Result.overflow;
|
||||
return Result.result;
|
||||
}
|
||||
|
||||
/*
|
||||
uint umulo(uint a, uint b, out bool overflow) {
|
||||
auto Result = llvm_umul_with_overflow(a, b);
|
||||
overflow = Result.overflow;
|
||||
return Result.result;
|
||||
}
|
||||
*/
|
||||
|
||||
void test(int function(int, int, out bool) fn,
|
||||
int a, int b, int result_e, bool ovf_e) {
|
||||
version(PRINTF)
|
||||
printf("%8x :: %8x :: %8x :: %.*s\n", a, b, result_e,
|
||||
(ovf_e ? "true" : "false"));
|
||||
|
||||
bool ovf;
|
||||
int result = fn(a, b, ovf);
|
||||
|
||||
version(PRINTF)
|
||||
printf("____________________ %8x :: %.*s\n", result,
|
||||
(ovf ? "true" : "false"));
|
||||
|
||||
assert(ovf == ovf_e);
|
||||
assert(result == result_e);
|
||||
}
|
||||
|
||||
void main() {
|
||||
test(&saddo, int.min, int.min, int.min + int.min, true);
|
||||
test(&saddo, int.min, int.max, int.min + int.max, false);
|
||||
test(&saddo, 1, int.max, 1 + int.max, true);
|
||||
test(&saddo, 1, 2, 3, false);
|
||||
test(&saddo, -1, -2, -3, false);
|
||||
|
||||
test(&uaddo, 0, uint.max, 0 + uint.max, false);
|
||||
test(&uaddo, 1, uint.max, 1 + uint.max, true);
|
||||
test(&uaddo, 1, 2, 3, false);
|
||||
|
||||
test(&smulo, int.min, int.min, int.min * int.min, true);
|
||||
test(&smulo, int.min, int.max, int.min * int.max, true);
|
||||
test(&smulo, int.max, int.max, int.max * int.max, true);
|
||||
test(&smulo, 1, int.max, 1 * int.max, false);
|
||||
test(&smulo, 2, int.max/2, 2 * (int.max/2), false);
|
||||
test(&smulo, 2, int.max/2 + 1, 2 * (int.max/2 + 1), true);
|
||||
test(&smulo, 2, int.min/2, 2 * (int.min/2), false);
|
||||
test(&smulo, 2, int.min/2 - 1, 2 * (int.min/2 - 1), true);
|
||||
test(&smulo, 1, 2, 2, false);
|
||||
test(&smulo, -1, -2, 2, false);
|
||||
}
|
||||
Reference in New Issue
Block a user