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:
Frits van Bommel
2009-05-16 13:06:49 +02:00
parent 62e8384da0
commit 541f3723cd
7 changed files with 219 additions and 60 deletions

39
gen/abi-generic.h Normal file
View 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

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
//

View 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);
}