From d28ab7e8022a5ccb399d0c54e186cb4f722c04bd Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 27 May 2013 21:48:25 +0000 Subject: [PATCH] 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 --- clang/lib/CodeGen/TargetInfo.cpp | 93 ++++++++++++++++++++++++++++++++ clang/test/CodeGen/sparcv9-abi.c | 58 ++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 clang/test/CodeGen/sparcv9-abi.c diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 74db0e37a4da..222ef1429df4 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()) + 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 000000000000..eb515e09e74a --- /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; +} +