diff options
author | Jakob Stoklund Olesen <stoklund@2pi.dk> | 2013-05-27 21:48:25 +0000 |
---|---|---|
committer | Jakob Stoklund Olesen <stoklund@2pi.dk> | 2013-05-27 21:48:25 +0000 |
commit | d28ab7e8022a5ccb399d0c54e186cb4f722c04bd (patch) | |
tree | 6c6ab529d3fc86865e5f41d7595f083dec8ff69e /clang | |
parent | 9d9e1fc47953f6eac8855b17968322c63baed2d8 (diff) | |
download | bcm5719-llvm-d28ab7e8022a5ccb399d0c54e186cb4f722c04bd.tar.gz bcm5719-llvm-d28ab7e8022a5ccb399d0c54e186cb4f722c04bd.zip |
Add a SparcV9ABIInfo class for handling the standard SPARC v9 ABI.
- All integer arguments smaller than 64 bits are extended.
- Large structs are passed indirectly, not using 'byval'.
- Structs up to 32 bytes in size are returned in registers.
Some things are not implemented yet:
- EmitVAArg can be implemented in terms of the va_arg instruction.
- When structs are passed in registers, float members require special
handling because they are passed in the floating point registers.
- Structs are left-aligned when passed in registers. This may require
padding.
llvm-svn: 182745
Diffstat (limited to 'clang')
-rw-r--r-- | clang/lib/CodeGen/TargetInfo.cpp | 93 | ||||
-rw-r--r-- | clang/test/CodeGen/sparcv9-abi.c | 58 |
2 files changed, 151 insertions, 0 deletions
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 74db0e37a4d..222ef1429df 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -5125,6 +5125,97 @@ llvm::Value *HexagonABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, } +//===----------------------------------------------------------------------===// +// SPARC v9 ABI Implementation. +// Based on the SPARC Compliance Definition version 2.4.1. +// +// Function arguments a mapped to a nominal "parameter array" and promoted to +// registers depending on their type. Each argument occupies 8 or 16 bytes in +// the array, structs larger than 16 bytes are passed indirectly. +// +// One case requires special care: +// +// struct mixed { +// int i; +// float f; +// }; +// +// When a struct mixed is passed by value, it only occupies 8 bytes in the +// parameter array, but the int is passed in an integer register, and the float +// is passed in a floating point register. This is represented as two arguments +// with the LLVM IR inreg attribute: +// +// declare void f(i32 inreg %i, float inreg %f) +// +// The code generator will only allocate 4 bytes from the parameter array for +// the inreg arguments. All other arguments are allocated a multiple of 8 +// bytes. +// +namespace { +class SparcV9ABIInfo : public ABIInfo { +public: + SparcV9ABIInfo(CodeGenTypes &CGT) : ABIInfo(CGT) {} + +private: + ABIArgInfo classifyType(QualType RetTy, unsigned SizeLimit) const; + virtual void computeInfo(CGFunctionInfo &FI) const; + virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty, + CodeGenFunction &CGF) const; +}; +} // end anonymous namespace + +ABIArgInfo +SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit) const { + if (Ty->isVoidType()) + return ABIArgInfo::getIgnore(); + + uint64_t Size = getContext().getTypeSize(Ty); + + // Anything too big to fit in registers is passed with an explicit indirect + // pointer / sret pointer. + if (Size > SizeLimit) + return ABIArgInfo::getIndirect(0, /*ByVal=*/false); + + // Treat an enum type as its underlying type. + if (const EnumType *EnumTy = Ty->getAs<EnumType>()) + Ty = EnumTy->getDecl()->getIntegerType(); + + // Integer types smaller than a register are extended. + if (Size < 64 && Ty->isIntegerType()) + return ABIArgInfo::getExtend(); + + // Other non-aggregates go in registers. + if (!isAggregateTypeForABI(Ty)) + return ABIArgInfo::getDirect(); + + // This is a small aggregate type that should be passed in registers. + // FIXME: Compute the correct coersion type. + // FIXME: Ensure any float members are passed in float registers. + return ABIArgInfo::getDirect(); +} + +llvm::Value *SparcV9ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, + CodeGenFunction &CGF) const { + // FIXME: Implement with va_arg. + return 0; +} + +void SparcV9ABIInfo::computeInfo(CGFunctionInfo &FI) const { + FI.getReturnInfo() = classifyType(FI.getReturnType(), 32 * 8); + for (CGFunctionInfo::arg_iterator it = FI.arg_begin(), ie = FI.arg_end(); + it != ie; ++it) + it->info = classifyType(it->type, 16 * 8); +} + +namespace { +class SparcV9TargetCodeGenInfo : public TargetCodeGenInfo { +public: + SparcV9TargetCodeGenInfo(CodeGenTypes &CGT) + : TargetCodeGenInfo(new SparcV9ABIInfo(CGT)) {} +}; +} // end anonymous namespace + + const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { if (TheTargetCodeGenInfo) return *TheTargetCodeGenInfo; @@ -5240,5 +5331,7 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { } case llvm::Triple::hexagon: return *(TheTargetCodeGenInfo = new HexagonTargetCodeGenInfo(Types)); + case llvm::Triple::sparcv9: + return *(TheTargetCodeGenInfo = new SparcV9TargetCodeGenInfo(Types)); } } diff --git a/clang/test/CodeGen/sparcv9-abi.c b/clang/test/CodeGen/sparcv9-abi.c new file mode 100644 index 00000000000..eb515e09e74 --- /dev/null +++ b/clang/test/CodeGen/sparcv9-abi.c @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -triple sparcv9-unknown-linux -emit-llvm %s -o - | FileCheck %s + +// CHECK: define void @f_void() +void f_void(void) {} + +// Arguments and return values smaller than the word size are extended. + +// CHECK: define signext i32 @f_int_1(i32 signext %x) +int f_int_1(int x) { return x; } + +// CHECK: define zeroext i32 @f_int_2(i32 zeroext %x) +unsigned f_int_2(unsigned x) { return x; } + +// CHECK: define i64 @f_int_3(i64 %x) +long long f_int_3(long long x) { return x; } + +// CHECK: define signext i8 @f_int_4(i8 signext %x) +char f_int_4(char x) { return x; } + +// Small structs are passed in registers. +struct small { + int *a, *b; +}; + +// CHECK: define %struct.small @f_small(i32* %x.coerce0, i32* %x.coerce1) +struct small f_small(struct small x) { + x.a += *x.b; + x.b = 0; + return x; +} + +// Medium-sized structs are passed indirectly, but can be returned in registers. +struct medium { + int *a, *b; + int *c, *d; +}; + +// CHECK: define %struct.medium @f_medium(%struct.medium* %x) +struct medium f_medium(struct medium x) { + x.a += *x.b; + x.b = 0; + return x; +} + +// Large structs are also returned indirectly. +struct large { + int *a, *b; + int *c, *d; + int x; +}; + +// CHECK: define void @f_large(%struct.large* noalias sret %agg.result, %struct.large* %x) +struct large f_large(struct large x) { + x.a += *x.b; + x.b = 0; + return x; +} + |