From 541f3723cd020cda38d330dfb0ca495224971be1 Mon Sep 17 00:00:00 2001 From: Frits van Bommel Date: Sat, 16 May 2009 13:06:49 +0200 Subject: [PATCH] Intrinsics shouldn't see struct padding, so use a special TargetABI for them that removes it. This unbreaks the `llvm_*_with_overflow` intrinsics. --- gen/abi-generic.h | 39 ++++++++++++++++ gen/abi-x86-64.cpp | 34 +------------- gen/abi.cpp | 77 ++++++++++++++++++++++++++++++++ gen/abi.h | 3 ++ gen/functions.cpp | 45 +++++++++---------- runtime/import/ldc/intrinsics.di | 4 +- tests/mini/intrinsics_ovf.d | 77 ++++++++++++++++++++++++++++++++ 7 files changed, 219 insertions(+), 60 deletions(-) create mode 100644 gen/abi-generic.h create mode 100644 tests/mini/intrinsics_ovf.d diff --git a/gen/abi-generic.h b/gen/abi-generic.h new file mode 100644 index 00000000..c32b70dc --- /dev/null +++ b/gen/abi-generic.h @@ -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 diff --git a/gen/abi-x86-64.cpp b/gen/abi-x86-64.cpp index 474f3ef1..1ba42441 100644 --- a/gen/abi-x86-64.cpp +++ b/gen/abi-x86-64.cpp @@ -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 @@ -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; }; diff --git a/gen/abi.cpp b/gen/abi.cpp index b7a6c6df..4be4f3a2 100644 --- a/gen/abi.cpp +++ b/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; +} diff --git a/gen/abi.h b/gen/abi.h index 5602aa43..56f055fb 100644 --- a/gen/abi.h +++ b/gen/abi.h @@ -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; diff --git a/gen/functions.cpp b/gen/functions.cpp index 198b9875..bc2251c6 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -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 argtypes; diff --git a/runtime/import/ldc/intrinsics.di b/runtime/import/ldc/intrinsics.di index fa5f3dd3..5a27a40a 100644 --- a/runtime/import/ldc/intrinsics.di +++ b/runtime/import/ldc/intrinsics.di @@ -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); // diff --git a/tests/mini/intrinsics_ovf.d b/tests/mini/intrinsics_ovf.d new file mode 100644 index 00000000..34f6836b --- /dev/null +++ b/tests/mini/intrinsics_ovf.d @@ -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); +}