changeset 1359:34f2fd925de3

Intrinsics shouldn't see struct padding, so use a special TargetABI for them that removes it. This unbreaks the `llvm_*_with_overflow` intrinsics.
author Frits van Bommel <fvbommel wxs.nl>
date Sat, 16 May 2009 13:06:49 +0200
parents 48747003a5de
children 015e00affbb9
files gen/abi-generic.h gen/abi-x86-64.cpp gen/abi.cpp gen/abi.h gen/functions.cpp runtime/import/ldc/intrinsics.di tests/mini/intrinsics_ovf.d
diffstat 7 files changed, 219 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- /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
--- 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 <cassert>
@@ -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;
 };
--- 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;
+}
--- 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;
--- 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<const LLType*> argtypes;
--- 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);
 
 
 //
--- /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);
+}