# HG changeset patch # User Frits van Bommel # Date 1242472009 -7200 # Node ID 34f2fd925de3caa7abeb40db70a7d6ecfffd1e28 # Parent 48747003a5de424e1198734b7507273a1aad89fe Intrinsics shouldn't see struct padding, so use a special TargetABI for them that removes it. This unbreaks the `llvm_*_with_overflow` intrinsics. diff -r 48747003a5de -r 34f2fd925de3 gen/abi-generic.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gen/abi-generic.h Sat May 16 13:06:49 2009 +0200 @@ -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 -r 48747003a5de -r 34f2fd925de3 gen/abi-x86-64.cpp --- a/gen/abi-x86-64.cpp Fri May 15 17:17:20 2009 +0200 +++ b/gen/abi-x86-64.cpp Sat May 16 13:06:49 2009 +0200 @@ -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 @@ }; -/// 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 -r 48747003a5de -r 34f2fd925de3 gen/abi.cpp --- a/gen/abi.cpp Fri May 15 17:17:20 2009 +0200 +++ b/gen/abi.cpp Sat May 16 13:06:49 2009 +0200 @@ -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 @@ 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 -r 48747003a5de -r 34f2fd925de3 gen/abi.h --- a/gen/abi.h Fri May 15 17:17:20 2009 +0200 +++ b/gen/abi.h Sat May 16 13:06:49 2009 +0200 @@ -33,7 +33,10 @@ // 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 -r 48747003a5de -r 34f2fd925de3 gen/functions.cpp --- a/gen/functions.cpp Fri May 15 17:17:20 2009 +0200 +++ b/gen/functions.cpp Sat May 16 13:06:49 2009 +0200 @@ -40,10 +40,9 @@ 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 @@ 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 @@ 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 @@ // 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 @@ // 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 -r 48747003a5de -r 34f2fd925de3 runtime/import/ldc/intrinsics.di --- a/runtime/import/ldc/intrinsics.di Fri May 15 17:17:20 2009 +0200 +++ b/runtime/import/ldc/intrinsics.di Sat May 16 13:06:49 2009 +0200 @@ -396,8 +396,8 @@ * * 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 -r 48747003a5de -r 34f2fd925de3 tests/mini/intrinsics_ovf.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/mini/intrinsics_ovf.d Sat May 16 13:06:49 2009 +0200 @@ -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); +}