Index: lib/Target/Mips/MipsInstrInfo.td =================================================================== --- lib/Target/Mips/MipsInstrInfo.td +++ lib/Target/Mips/MipsInstrInfo.td @@ -162,8 +162,10 @@ AssemblerPredicate<"FeatureMips32r2">; def HasMips64 : Predicate<"Subtarget.hasMips64()">, AssemblerPredicate<"FeatureMips64">; -def NotMips64 : Predicate<"!Subtarget.hasMips64()">, - AssemblerPredicate<"!FeatureMips64">; +def IsGP32 : Predicate<"!Subtarget.isGP64()">, + AssemblerPredicate<"!FeatureGP64Bit">; +def IsGP64 : Predicate<"Subtarget.isGP64()">, + AssemblerPredicate<"FeatureGP64Bit">; def HasMips64r2 : Predicate<"Subtarget.hasMips64r2()">, AssemblerPredicate<"FeatureMips64r2">; def IsN64 : Predicate<"Subtarget.isABI_N64()">, @@ -1171,7 +1173,7 @@ //===----------------------------------------------------------------------===// def : InstAlias<"move $dst, $src", (ADDu GPR32Opnd:$dst, GPR32Opnd:$src,ZERO), 1>, - Requires<[NotMips64, NotInMicroMips]>; + Requires<[IsGP32, NotInMicroMips]>; def : InstAlias<"bal $offset", (BGEZAL ZERO, brtarget:$offset), 0>; def : InstAlias<"addu $rs, $rt, $imm", (ADDiu GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), 0>; Index: test/CodeGen/Mips/cconv/arguments-float.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/arguments-float.ll @@ -0,0 +1,222 @@ +; RUN: llc -march=mips -relocation-model=static -soft-float < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32BE %s +; RUN: llc -march=mipsel -relocation-model=static -soft-float < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32LE %s + +; RUN-TODO: llc -march=mips64 -relocation-model=static -soft-float -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -relocation-model=static -soft-float -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s + +; RUN: llc -march=mips64 -relocation-model=static -soft-float -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -soft-float -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=NEW %s + +; RUN: llc -march=mips64 -relocation-model=static -soft-float -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -soft-float -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=NEW %s + +; Test the floating point arguments for all ABI's and byte orders as specified +; by section 5 of MD00305 (MIPS ABIs Described). +; +; N32/N64 are identical in this area so their checks have been combined into +; the 'NEW' prefix (the N stands for New). + +@bytes = global [11 x i8] zeroinitializer +@dwords = global [11 x i64] zeroinitializer +@floats = global [11 x float] zeroinitializer +@doubles = global [11 x double] zeroinitializer + +define void @double_args(double %a, double %b, double %c, double %d, double %e, + double %f, double %g, double %h, double %i) nounwind { +entry: + %0 = getelementptr [11 x double]* @doubles, i32 0, i32 1 + store volatile double %a, double* %0 + %1 = getelementptr [11 x double]* @doubles, i32 0, i32 2 + store volatile double %b, double* %1 + %2 = getelementptr [11 x double]* @doubles, i32 0, i32 3 + store volatile double %c, double* %2 + %3 = getelementptr [11 x double]* @doubles, i32 0, i32 4 + store volatile double %d, double* %3 + %4 = getelementptr [11 x double]* @doubles, i32 0, i32 5 + store volatile double %e, double* %4 + %5 = getelementptr [11 x double]* @doubles, i32 0, i32 6 + store volatile double %f, double* %5 + %6 = getelementptr [11 x double]* @doubles, i32 0, i32 7 + store volatile double %g, double* %6 + %7 = getelementptr [11 x double]* @doubles, i32 0, i32 8 + store volatile double %h, double* %7 + %8 = getelementptr [11 x double]* @doubles, i32 0, i32 9 + store volatile double %i, double* %8 + ret void +} + +; ALL-LABEL: double_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(doubles)( + +; The first four arguments are the same in O32/N32/N64. +; The first argument is floating point but soft-float is enabled so floating +; point registers are not used. +; O32-DAG: sw $4, 8([[R2]]) +; O32-DAG: sw $5, 12([[R2]]) +; NEW-DAG: sd $4, 8([[R2]]) + +; O32-DAG: sw $6, 16([[R2]]) +; O32-DAG: sw $7, 20([[R2]]) +; NEW-DAG: sd $5, 16([[R2]]) + +; O32 has run out of argument registers and starts using the stack +; O32-DAG: lw [[R3:\$([0-9]+|gp)]], 24($sp) +; O32-DAG: lw [[R4:\$([0-9]+|gp)]], 28($sp) +; O32-DAG: sw [[R3]], 24([[R2]]) +; O32-DAG: sw [[R4]], 28([[R2]]) +; NEW-DAG: sd $6, 24([[R2]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 32($sp) +; O32-DAG: lw [[R4:\$[0-9]+]], 36($sp) +; O32-DAG: sw [[R3]], 32([[R2]]) +; O32-DAG: sw [[R4]], 36([[R2]]) +; NEW-DAG: sd $7, 32([[R2]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 40($sp) +; O32-DAG: lw [[R4:\$[0-9]+]], 44($sp) +; O32-DAG: sw [[R3]], 40([[R2]]) +; O32-DAG: sw [[R4]], 44([[R2]]) +; NEW-DAG: sd $8, 40([[R2]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 48($sp) +; O32-DAG: lw [[R4:\$[0-9]+]], 52($sp) +; O32-DAG: sw [[R3]], 48([[R2]]) +; O32-DAG: sw [[R4]], 52([[R2]]) +; NEW-DAG: sd $9, 48([[R2]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 56($sp) +; O32-DAG: lw [[R4:\$[0-9]+]], 60($sp) +; O32-DAG: sw [[R3]], 56([[R2]]) +; O32-DAG: sw [[R4]], 60([[R2]]) +; NEW-DAG: sd $10, 56([[R2]]) + +; N32/N64 have run out of registers and starts using the stack too +; O32-DAG: lw [[R3:\$[0-9]+]], 64($sp) +; O32-DAG: lw [[R4:\$[0-9]+]], 68($sp) +; O32-DAG: sw [[R3]], 64([[R2]]) +; O32-DAG: sw [[R4]], 68([[R2]]) +; NEW-DAG: ld [[R3:\$[0-9]+]], 0($sp) +; NEW-DAG: sd $11, 64([[R2]]) + +define void @float_args(float %a, float %b, float %c, float %d, float %e, + float %f, float %g, float %h, float %i, float %j) + nounwind { +entry: + %0 = getelementptr [11 x float]* @floats, i32 0, i32 1 + store volatile float %a, float* %0 + %1 = getelementptr [11 x float]* @floats, i32 0, i32 2 + store volatile float %b, float* %1 + %2 = getelementptr [11 x float]* @floats, i32 0, i32 3 + store volatile float %c, float* %2 + %3 = getelementptr [11 x float]* @floats, i32 0, i32 4 + store volatile float %d, float* %3 + %4 = getelementptr [11 x float]* @floats, i32 0, i32 5 + store volatile float %e, float* %4 + %5 = getelementptr [11 x float]* @floats, i32 0, i32 6 + store volatile float %f, float* %5 + %6 = getelementptr [11 x float]* @floats, i32 0, i32 7 + store volatile float %g, float* %6 + %7 = getelementptr [11 x float]* @floats, i32 0, i32 8 + store volatile float %h, float* %7 + %8 = getelementptr [11 x float]* @floats, i32 0, i32 9 + store volatile float %i, float* %8 + %9 = getelementptr [11 x float]* @floats, i32 0, i32 10 + store volatile float %j, float* %9 + ret void +} + +; ALL-LABEL: float_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(floats)( + +; The first four arguments are the same in O32/N32/N64. +; The first argument isn't floating point so floating point registers are not +; used. +; MD00305 and GCC disagree on this one. MD00305 says that floats are treated +; as 8-byte aligned and occupy two slots on O32. GCC is treating them as 4-byte +; aligned and occupying one slot. We'll use GCC's definition. +; ALL-DAG: sw $4, 4([[R2]]) +; ALL-DAG: sw $5, 8([[R2]]) +; ALL-DAG: sw $6, 12([[R2]]) +; ALL-DAG: sw $7, 16([[R2]]) + +; O32 has run out of argument registers and starts using the stack +; O32-DAG: lw [[R3:\$[0-9]+]], 16($sp) +; O32-DAG: sw [[R3]], 20([[R2]]) +; NEW-DAG: sw $8, 20([[R2]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 20($sp) +; O32-DAG: sw [[R3]], 24([[R2]]) +; NEW-DAG: sw $9, 24([[R2]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 24($sp) +; O32-DAG: sw [[R3]], 28([[R2]]) +; NEW-DAG: sw $10, 28([[R2]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 28($sp) +; O32-DAG: sw [[R3]], 32([[R2]]) +; NEW-DAG: sw $11, 32([[R2]]) + +; N32/N64 have run out of registers and start using the stack too +; O32-DAG: lw [[R3:\$[0-9]+]], 32($sp) +; O32-DAG: sw [[R3]], 36([[R2]]) +; NEW-DAG: lw [[R3:\$[0-9]+]], 0($sp) +; NEW-DAG: sw [[R3]], 36([[R2]]) + +define void @double_arg2(i8 %a, double %b) nounwind { +entry: + %0 = getelementptr [11 x i8]* @bytes, i32 0, i32 1 + store volatile i8 %a, i8* %0 + %1 = getelementptr [11 x double]* @doubles, i32 0, i32 1 + store volatile double %b, double* %1 + ret void +} + +; ALL-LABEL: double_arg2: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes) +; SYM64-DAG: ld [[R1:\$[0-9]]], %got_disp(bytes)( +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(doubles)( + +; The first four arguments are the same in O32/N32/N64. +; The first argument isn't floating point so floating point registers are not +; used. +; The second slot is insufficiently aligned for double on O32 so it is skipped. +; Also, double occupies two slots on O32 and only one for N32/N64. +; ALL-DAG: sb $4, 1([[R1]]) +; O32-DAG: sw $6, 8([[R2]]) +; O32-DAG: sw $7, 12([[R2]]) +; NEW-DAG: sd $5, 8([[R2]]) + +define void @float_arg2(i8 %a, float %b) nounwind { +entry: + %0 = getelementptr [11 x i8]* @bytes, i32 0, i32 1 + store volatile i8 %a, i8* %0 + %1 = getelementptr [11 x float]* @floats, i32 0, i32 1 + store volatile float %b, float* %1 + ret void +} + +; ALL-LABEL: float_arg2: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes) +; SYM64-DAG: ld [[R1:\$[0-9]]], %got_disp(bytes)( +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(floats)( + +; The first four arguments are the same in O32/N32/N64. +; The first argument isn't floating point so floating point registers are not +; used. +; MD00305 and GCC disagree on this one. MD00305 says that floats are treated +; as 8-byte aligned and occupy two slots on O32. GCC is treating them as 4-byte +; aligned and occupying one slot. We'll use GCC's definition. +; ALL-DAG: sb $4, 1([[R1]]) +; ALL-DAG: sw $5, 4([[R2]]) Index: test/CodeGen/Mips/cconv/arguments-fp128.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/arguments-fp128.ll @@ -0,0 +1,51 @@ +; RUN: llc -march=mips64 -relocation-model=static -soft-float -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 %s +; RUN: llc -march=mips64el -relocation-model=static -soft-float -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 %s + +; RUN: llc -march=mips64 -relocation-model=static -soft-float -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 %s +; RUN: llc -march=mips64el -relocation-model=static -soft-float -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 %s + +; Test the fp128 arguments for all ABI's and byte orders as specified +; by section 2 of the MIPSpro N32 Handbook. +; +; O32 is not tested because long double is the same as double on O32. + +@ldoubles = global [11 x fp128] zeroinitializer + +define void @ldouble_args(fp128 %a, fp128 %b, fp128 %c, fp128 %d, fp128 %e) nounwind { +entry: + %0 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 1 + store volatile fp128 %a, fp128* %0 + %1 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 2 + store volatile fp128 %b, fp128* %1 + %2 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 3 + store volatile fp128 %c, fp128* %2 + %3 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 4 + store volatile fp128 %d, fp128* %3 + %4 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 5 + store volatile fp128 %e, fp128* %4 + ret void +} + +; ALL-LABEL: ldouble_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(ldoubles) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(ldoubles)( + +; The first four arguments are the same in N32/N64. +; The first argument is floating point but soft-float is enabled so floating +; point registers are not used. +; ALL-DAG: sd $4, 16([[R2]]) +; ALL-DAG: sd $5, 24([[R2]]) +; ALL-DAG: sd $6, 32([[R2]]) +; ALL-DAG: sd $7, 40([[R2]]) +; ALL-DAG: sd $8, 48([[R2]]) +; ALL-DAG: sd $9, 56([[R2]]) +; ALL-DAG: sd $10, 64([[R2]]) +; ALL-DAG: sd $11, 72([[R2]]) + +; N32/N64 have run out of registers and starts using the stack too +; ALL-DAG: ld [[R3:\$[0-9]+]], 0($sp) +; ALL-DAG: ld [[R4:\$[0-9]+]], 8($sp) +; ALL-DAG: sd [[R3]], 80([[R2]]) +; ALL-DAG: sd [[R4]], 88([[R2]]) Index: test/CodeGen/Mips/cconv/arguments-hard-float-varargs.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/arguments-hard-float-varargs.ll @@ -0,0 +1,157 @@ +; RUN: llc -march=mips -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32BE %s +; RUN: llc -march=mipsel -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32LE %s + +; RUN-TODO: llc -march=mips64 -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=N32 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=N32 --check-prefix=NEW %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=N64 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=N64 --check-prefix=NEW %s + +; Test the effect of varargs on floating point types in the non-variable part +; of the argument list as specified by section 2 of the MIPSpro N32 Handbook. +; +; N32/N64 are almost identical in this area so many of their checks have been +; combined into the 'NEW' prefix (the N stands for New). +; +; On O32, varargs prevents all FPU argument register usage. This contradicts +; the N32 handbook, but agrees with the SYSV ABI and GCC's behaviour. + +@floats = global [11 x float] zeroinitializer +@doubles = global [11 x double] zeroinitializer + +define void @double_args(double %a, ...) + nounwind { +entry: + %0 = getelementptr [11 x double]* @doubles, i32 0, i32 1 + store volatile double %a, double* %0 + + %ap = alloca i8* + %ap2 = bitcast i8** %ap to i8* + call void @llvm.va_start(i8* %ap2) + %b = va_arg i8** %ap, double + %1 = getelementptr [11 x double]* @doubles, i32 0, i32 2 + store volatile double %b, double* %1 + ret void +} + +; ALL-LABEL: double_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(doubles)( + +; O32 forbids using floating point registers for the non-variable portion. +; N32/N64 allow it. +; O32BE-DAG: mtc1 $5, [[FTMP1:\$f[0-9]*[02468]+]] +; O32BE-DAG: mtc1 $4, [[FTMP2:\$f[0-9]*[13579]+]] +; O32LE-DAG: mtc1 $4, [[FTMP1:\$f[0-9]*[02468]+]] +; O32LE-DAG: mtc1 $5, [[FTMP2:\$f[0-9]*[13579]+]] +; O32-DAG: sdc1 [[FTMP1]], 8([[R2]]) +; NEW-DAG: sdc1 $f12, 8([[R2]]) + +; The varargs portion is dumped to stack +; O32-DAG: sw $6, 16($sp) +; O32-DAG: sw $7, 20($sp) +; NEW-DAG: sd $5, 8($sp) +; NEW-DAG: sd $6, 16($sp) +; NEW-DAG: sd $7, 24($sp) +; NEW-DAG: sd $8, 32($sp) +; NEW-DAG: sd $9, 40($sp) +; NEW-DAG: sd $10, 48($sp) +; NEW-DAG: sd $11, 56($sp) + +; Get the varargs pointer +; O32 has 4 bytes padding, 4 bytes for the varargs pointer, and 8 bytes reserved +; for arguments 1 and 2. +; N32/N64 has 8 bytes for the varargs pointer, and no reserved area. +; O32-DAG: addiu [[VAPTR:\$[0-9]+]], $sp, 16 +; O32-DAG: sw [[VAPTR]], 4($sp) +; N32-DAG: addiu [[VAPTR:\$[0-9]+]], $sp, 8 +; N32-DAG: sw [[VAPTR]], 4($sp) +; N64-DAG: daddiu [[VAPTR:\$[0-9]+]], $sp, 8 +; N64-DAG: sd [[VAPTR]], 0($sp) + +; Increment the pointer then get the varargs arg +; LLVM will rebind the load to the stack pointer instead of the varargs pointer +; during lowering. This is fine and doesn't change the behaviour. +; O32-DAG: addiu [[VAPTR]], [[VAPTR]], 8 +; O32-DAG: sw [[VAPTR]], 4($sp) +; N32-DAG: addiu [[VAPTR]], [[VAPTR]], 8 +; N32-DAG: sw [[VAPTR]], 4($sp) +; N64-DAG: daddiu [[VAPTR]], [[VAPTR]], 8 +; N64-DAG: sd [[VAPTR]], 0($sp) +; O32-DAG: ldc1 [[FTMP1:\$f[0-9]+]], 16($sp) +; NEW-DAG: ldc1 [[FTMP1:\$f[0-9]+]], 8($sp) +; ALL-DAG: sdc1 [[FTMP1]], 16([[R2]]) + +define void @float_args(float %a, ...) nounwind { +entry: + %0 = getelementptr [11 x float]* @floats, i32 0, i32 1 + store volatile float %a, float* %0 + + %ap = alloca i8* + %ap2 = bitcast i8** %ap to i8* + call void @llvm.va_start(i8* %ap2) + %b = va_arg i8** %ap, float + %1 = getelementptr [11 x float]* @floats, i32 0, i32 2 + store volatile float %b, float* %1 + ret void +} + +; ALL-LABEL: float_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(floats)( + +; The first four arguments are the same in O32/N32/N64. +; The non-variable portion should be unaffected. +; O32-DAG: sw $4, 4([[R2]]) +; NEW-DAG: swc1 $f12, 4([[R2]]) + +; The varargs portion is dumped to stack +; O32-DAG: sw $5, 12($sp) +; O32-DAG: sw $6, 16($sp) +; O32-DAG: sw $7, 20($sp) +; NEW-DAG: sd $5, 8($sp) +; NEW-DAG: sd $6, 16($sp) +; NEW-DAG: sd $7, 24($sp) +; NEW-DAG: sd $8, 32($sp) +; NEW-DAG: sd $9, 40($sp) +; NEW-DAG: sd $10, 48($sp) +; NEW-DAG: sd $11, 56($sp) + +; Get the varargs pointer +; O32 has 4 bytes padding, 4 bytes for the varargs pointer, and should have 8 +; bytes reserved for arguments 1 and 2 (the first float arg) but as discussed in +; arguments-float.ll, GCC doesn't agree with MD00305 and treats floats as 4 +; bytes so we only have 12 bytes total. +; N32/N64 has 8 bytes for the varargs pointer, and no reserved area. +; O32-DAG: addiu [[VAPTR:\$[0-9]+]], $sp, 12 +; O32-DAG: sw [[VAPTR]], 4($sp) +; N32-DAG: addiu [[VAPTR:\$[0-9]+]], $sp, 8 +; N32-DAG: sw [[VAPTR]], 4($sp) +; N64-DAG: daddiu [[VAPTR:\$[0-9]+]], $sp, 8 +; N64-DAG: sd [[VAPTR]], 0($sp) + +; Increment the pointer then get the varargs arg +; LLVM will rebind the load to the stack pointer instead of the varargs pointer +; during lowering. This is fine and doesn't change the behaviour. +; N32/N64 is using ori instead of addiu/daddiu but (although odd) this is fine +; since the stack is always aligned. +; O32-DAG: addiu [[VAPTR]], [[VAPTR]], 4 +; O32-DAG: sw [[VAPTR]], 4($sp) +; N32-DAG: ori [[VAPTR]], [[VAPTR]], 4 +; N32-DAG: sw [[VAPTR]], 4($sp) +; N64-DAG: ori [[VAPTR]], [[VAPTR]], 4 +; N64-DAG: sd [[VAPTR]], 0($sp) +; O32-DAG: lwc1 [[FTMP1:\$f[0-9]+]], 12($sp) +; NEW-DAG: lwc1 [[FTMP1:\$f[0-9]+]], 8($sp) +; ALL-DAG: swc1 [[FTMP1]], 8([[R2]]) + +declare void @llvm.va_start(i8*) +declare void @llvm.va_copy(i8*, i8*) +declare void @llvm.va_end(i8*) Index: test/CodeGen/Mips/cconv/arguments-hard-float.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/arguments-hard-float.ll @@ -0,0 +1,211 @@ +; RUN: llc -march=mips -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32BE %s +; RUN: llc -march=mipsel -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32LE %s + +; RUN-TODO: llc -march=mips64 -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=NEW %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=NEW %s + +; Test the floating point arguments for all ABI's and byte orders as specified +; by section 5 of MD00305 (MIPS ABIs Described). +; +; N32/N64 are identical in this area so their checks have been combined into +; the 'NEW' prefix (the N stands for New). + +@bytes = global [11 x i8] zeroinitializer +@dwords = global [11 x i64] zeroinitializer +@floats = global [11 x float] zeroinitializer +@doubles = global [11 x double] zeroinitializer + +define void @double_args(double %a, double %b, double %c, double %d, double %e, + double %f, double %g, double %h, double %i) nounwind { +entry: + %0 = getelementptr [11 x double]* @doubles, i32 0, i32 1 + store volatile double %a, double* %0 + %1 = getelementptr [11 x double]* @doubles, i32 0, i32 2 + store volatile double %b, double* %1 + %2 = getelementptr [11 x double]* @doubles, i32 0, i32 3 + store volatile double %c, double* %2 + %3 = getelementptr [11 x double]* @doubles, i32 0, i32 4 + store volatile double %d, double* %3 + %4 = getelementptr [11 x double]* @doubles, i32 0, i32 5 + store volatile double %e, double* %4 + %5 = getelementptr [11 x double]* @doubles, i32 0, i32 6 + store volatile double %f, double* %5 + %6 = getelementptr [11 x double]* @doubles, i32 0, i32 7 + store volatile double %g, double* %6 + %7 = getelementptr [11 x double]* @doubles, i32 0, i32 8 + store volatile double %h, double* %7 + %8 = getelementptr [11 x double]* @doubles, i32 0, i32 9 + store volatile double %i, double* %8 + ret void +} + +; ALL-LABEL: double_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(doubles)( + +; The first argument is floating point so floating point registers are used. +; The first argument is the same for O32/N32/N64 but the second argument differs +; by register +; ALL-DAG: sdc1 $f12, 8([[R2]]) +; O32-DAG: sdc1 $f14, 16([[R2]]) +; NEW-DAG: sdc1 $f13, 16([[R2]]) + +; O32 has run out of argument registers and starts using the stack +; O32-DAG: ldc1 [[F1:\$f[0-9]+]], 16($sp) +; O32-DAG: sdc1 [[F1]], 24([[R2]]) +; NEW-DAG: sdc1 $f14, 24([[R2]]) +; O32-DAG: ldc1 [[F1:\$f[0-9]+]], 24($sp) +; O32-DAG: sdc1 [[F1]], 32([[R2]]) +; NEW-DAG: sdc1 $f15, 32([[R2]]) +; O32-DAG: ldc1 [[F1:\$f[0-9]+]], 32($sp) +; O32-DAG: sdc1 [[F1]], 40([[R2]]) +; NEW-DAG: sdc1 $f16, 40([[R2]]) +; O32-DAG: ldc1 [[F1:\$f[0-9]+]], 40($sp) +; O32-DAG: sdc1 [[F1]], 48([[R2]]) +; NEW-DAG: sdc1 $f17, 48([[R2]]) +; O32-DAG: ldc1 [[F1:\$f[0-9]+]], 48($sp) +; O32-DAG: sdc1 [[F1]], 56([[R2]]) +; NEW-DAG: sdc1 $f18, 56([[R2]]) +; O32-DAG: ldc1 [[F1:\$f[0-9]+]], 56($sp) +; O32-DAG: sdc1 [[F1]], 64([[R2]]) +; NEW-DAG: sdc1 $f19, 64([[R2]]) + +; N32/N64 have run out of registers and start using the stack too +; O32-DAG: ldc1 [[F1:\$f[0-9]+]], 64($sp) +; O32-DAG: sdc1 [[F1]], 72([[R2]]) +; NEW-DAG: ldc1 [[F1:\$f[0-9]+]], 0($sp) +; NEW-DAG: sdc1 [[F1]], 72([[R2]]) + +define void @float_args(float %a, float %b, float %c, float %d, float %e, + float %f, float %g, float %h, float %i) nounwind { +entry: + %0 = getelementptr [11 x float]* @floats, i32 0, i32 1 + store volatile float %a, float* %0 + %1 = getelementptr [11 x float]* @floats, i32 0, i32 2 + store volatile float %b, float* %1 + %2 = getelementptr [11 x float]* @floats, i32 0, i32 3 + store volatile float %c, float* %2 + %3 = getelementptr [11 x float]* @floats, i32 0, i32 4 + store volatile float %d, float* %3 + %4 = getelementptr [11 x float]* @floats, i32 0, i32 5 + store volatile float %e, float* %4 + %5 = getelementptr [11 x float]* @floats, i32 0, i32 6 + store volatile float %f, float* %5 + %6 = getelementptr [11 x float]* @floats, i32 0, i32 7 + store volatile float %g, float* %6 + %7 = getelementptr [11 x float]* @floats, i32 0, i32 8 + store volatile float %h, float* %7 + %8 = getelementptr [11 x float]* @floats, i32 0, i32 9 + store volatile float %i, float* %8 + ret void +} + +; ALL-LABEL: float_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(floats) +; SYM64-DAG: ld [[R1:\$[0-9]]], %got_disp(floats)( + +; The first argument is floating point so floating point registers are used. +; The first argument is the same for O32/N32/N64 but the second argument differs +; by register +; ALL-DAG: swc1 $f12, 4([[R1]]) +; O32-DAG: swc1 $f14, 8([[R1]]) +; NEW-DAG: swc1 $f13, 8([[R1]]) + +; O32 has run out of argument registers and (in theory) starts using the stack +; I've yet to find a reference in the documentation about this but GCC uses up +; the remaining two argument slots in the GPR's first. We'll do the same for +; compatibility. +; O32-DAG: sw $6, 12([[R1]]) +; NEW-DAG: swc1 $f14, 12([[R1]]) +; O32-DAG: sw $7, 16([[R1]]) +; NEW-DAG: swc1 $f15, 16([[R1]]) + +; O32 is definitely out of registers now and switches to the stack. +; O32-DAG: lwc1 [[F1:\$f[0-9]+]], 16($sp) +; O32-DAG: swc1 [[F1]], 20([[R1]]) +; NEW-DAG: swc1 $f16, 20([[R1]]) +; O32-DAG: lwc1 [[F1:\$f[0-9]+]], 20($sp) +; O32-DAG: swc1 [[F1]], 24([[R1]]) +; NEW-DAG: swc1 $f17, 24([[R1]]) +; O32-DAG: lwc1 [[F1:\$f[0-9]+]], 24($sp) +; O32-DAG: swc1 [[F1]], 28([[R1]]) +; NEW-DAG: swc1 $f18, 28([[R1]]) +; O32-DAG: lwc1 [[F1:\$f[0-9]+]], 28($sp) +; O32-DAG: swc1 [[F1]], 32([[R1]]) +; NEW-DAG: swc1 $f19, 32([[R1]]) + +; N32/N64 have run out of registers and start using the stack too +; O32-DAG: lwc1 [[F1:\$f[0-9]+]], 32($sp) +; O32-DAG: swc1 [[F1]], 36([[R1]]) +; NEW-DAG: lwc1 [[F1:\$f[0-9]+]], 0($sp) +; NEW-DAG: swc1 [[F1]], 36([[R1]]) + + +define void @double_arg2(i8 %a, double %b) nounwind { +entry: + %0 = getelementptr [11 x i8]* @bytes, i32 0, i32 1 + store volatile i8 %a, i8* %0 + %1 = getelementptr [11 x double]* @doubles, i32 0, i32 1 + store volatile double %b, double* %1 + ret void +} + +; ALL-LABEL: double_arg2: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes) +; SYM64-DAG: ld [[R1:\$[0-9]]], %got_disp(bytes)( +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(doubles)( + +; The first argument is the same in O32/N32/N64. +; ALL-DAG: sb $4, 1([[R1]]) + +; The first argument isn't floating point so floating point registers are not +; used in O32, but N32/N64 will still use them. +; The second slot is insufficiently aligned for double on O32 so it is skipped. +; Also, double occupies two slots on O32 and only one for N32/N64. +; O32LE-DAG: mtc1 $6, [[F1:\$f[0-9]*[02468]+]] +; O32LE-DAG: mtc1 $7, [[F2:\$f[0-9]*[13579]+]] +; O32BE-DAG: mtc1 $6, [[F2:\$f[0-9]*[13579]+]] +; O32BE-DAG: mtc1 $7, [[F1:\$f[0-9]*[02468]+]] +; O32-DAG: sdc1 [[F1]], 8([[R2]]) +; NEW-DAG: sdc1 $f13, 8([[R2]]) + +define void @float_arg2(i8 %a, float %b) nounwind { +entry: + %0 = getelementptr [11 x i8]* @bytes, i32 0, i32 1 + store volatile i8 %a, i8* %0 + %1 = getelementptr [11 x float]* @floats, i32 0, i32 1 + store volatile float %b, float* %1 + ret void +} + +; ALL-LABEL: float_arg2: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes) +; SYM64-DAG: ld [[R1:\$[0-9]]], %got_disp(bytes)( +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(floats)( + +; The first argument is the same in O32/N32/N64. +; ALL-DAG: sb $4, 1([[R1]]) + +; The first argument isn't floating point so floating point registers are not +; used in O32, but N32/N64 will still use them. +; MD00305 and GCC disagree on this one. MD00305 says that floats are treated +; as 8-byte aligned and occupy two slots on O32. GCC is treating them as 4-byte +; aligned and occupying one slot. We'll use GCC's definition. +; O32-DAG: sw $5, 4([[R2]]) +; NEW-DAG: swc1 $f13, 4([[R2]]) Index: test/CodeGen/Mips/cconv/arguments-hard-fp128.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/arguments-hard-fp128.ll @@ -0,0 +1,49 @@ +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 %s + +; Test the fp128 arguments for all ABI's and byte orders as specified +; by section 2 of the MIPSpro N32 Handbook. +; +; O32 is not tested because long double is the same as double on O32. + +@ldoubles = global [11 x fp128] zeroinitializer + +define void @ldouble_args(fp128 %a, fp128 %b, fp128 %c, fp128 %d, fp128 %e) nounwind { +entry: + %0 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 1 + store volatile fp128 %a, fp128* %0 + %1 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 2 + store volatile fp128 %b, fp128* %1 + %2 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 3 + store volatile fp128 %c, fp128* %2 + %3 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 4 + store volatile fp128 %d, fp128* %3 + %4 = getelementptr [11 x fp128]* @ldoubles, i32 0, i32 5 + store volatile fp128 %e, fp128* %4 + ret void +} + +; ALL-LABEL: ldouble_args: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(ldoubles) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(ldoubles)( + +; The first four arguments are the same in N32/N64. +; ALL-DAG: sdc1 $f12, 16([[R2]]) +; ALL-DAG: sdc1 $f13, 24([[R2]]) +; ALL-DAG: sdc1 $f14, 32([[R2]]) +; ALL-DAG: sdc1 $f15, 40([[R2]]) +; ALL-DAG: sdc1 $f16, 48([[R2]]) +; ALL-DAG: sdc1 $f17, 56([[R2]]) +; ALL-DAG: sdc1 $f18, 64([[R2]]) +; ALL-DAG: sdc1 $f19, 72([[R2]]) + +; N32/N64 have run out of registers and starts using the stack too +; ALL-DAG: ld [[R3:\$[0-9]+]], 0($sp) +; ALL-DAG: ld [[R4:\$[0-9]+]], 8($sp) +; ALL-DAG: sd [[R3]], 80([[R2]]) +; ALL-DAG: sd [[R4]], 88([[R2]]) Index: test/CodeGen/Mips/cconv/arguments.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/arguments.ll @@ -0,0 +1,170 @@ +; RUN: llc -march=mips -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32BE %s +; RUN: llc -march=mipsel -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 --check-prefix=O32LE %s + +; RUN-TODO: llc -march=mips64 -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=O32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM32 --check-prefix=NEW %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=NEW %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=SYM64 --check-prefix=NEW %s + +; Test the integer arguments for all ABI's and byte orders as specified by +; section 5 of MD00305 (MIPS ABIs Described). +; +; N32/N64 are identical in this area so their checks have been combined into +; the 'NEW' prefix (the N stands for New). +; +; Varargs are covered in arguments-hard-float-varargs.ll. + +@bytes = global [11 x i8] zeroinitializer +@dwords = global [11 x i64] zeroinitializer +@floats = global [11 x float] zeroinitializer +@doubles = global [11 x double] zeroinitializer + +define void @align_to_arg_slots(i8 %a, i8 %b, i8 %c, i8 %d, i8 %e, i8 %f, i8 %g, + i8 %h, i8 %i, i8 %j) nounwind { +entry: + %0 = getelementptr [11 x i8]* @bytes, i32 0, i32 1 + store volatile i8 %a, i8* %0 + %1 = getelementptr [11 x i8]* @bytes, i32 0, i32 2 + store volatile i8 %b, i8* %1 + %2 = getelementptr [11 x i8]* @bytes, i32 0, i32 3 + store volatile i8 %c, i8* %2 + %3 = getelementptr [11 x i8]* @bytes, i32 0, i32 4 + store volatile i8 %d, i8* %3 + %4 = getelementptr [11 x i8]* @bytes, i32 0, i32 5 + store volatile i8 %e, i8* %4 + %5 = getelementptr [11 x i8]* @bytes, i32 0, i32 6 + store volatile i8 %f, i8* %5 + %6 = getelementptr [11 x i8]* @bytes, i32 0, i32 7 + store volatile i8 %g, i8* %6 + %7 = getelementptr [11 x i8]* @bytes, i32 0, i32 8 + store volatile i8 %h, i8* %7 + %8 = getelementptr [11 x i8]* @bytes, i32 0, i32 9 + store volatile i8 %i, i8* %8 + %9 = getelementptr [11 x i8]* @bytes, i32 0, i32 10 + store volatile i8 %j, i8* %9 + ret void +} + +; ALL-LABEL: align_to_arg_slots: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes) +; SYM64-DAG: ld [[R1:\$[0-9]]], %got_disp(bytes)( + +; The first four arguments are the same in O32/N32/N64 +; ALL-DAG: sb $4, 1([[R1]]) +; ALL-DAG: sb $5, 2([[R1]]) +; ALL-DAG: sb $6, 3([[R1]]) +; ALL-DAG: sb $7, 4([[R1]]) + +; N32/N64 get an extra four arguments in registers +; O32 starts loading from the stack. The addresses start at 16 because space is +; always reserved for the first four arguments. +; O32-DAG: lw [[R3:\$[0-9]+]], 16($sp) +; O32-DAG: sb [[R3]], 5([[R1]]) +; NEW-DAG: sb $8, 5([[R1]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 20($sp) +; O32-DAG: sb [[R3]], 6([[R1]]) +; NEW-DAG: sb $9, 6([[R1]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 24($sp) +; O32-DAG: sb [[R3]], 7([[R1]]) +; NEW-DAG: sb $10, 7([[R1]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 28($sp) +; O32-DAG: sb [[R3]], 8([[R1]]) +; NEW-DAG: sb $11, 8([[R1]]) + +; O32/N32/N64 are accessing the stack at this point. +; Unlike O32, N32/N64 do not reserve space for the arguments. +; increase by 4 for O32 and 8 for N32/N64. +; O32-DAG: lw [[R3:\$[0-9]+]], 32($sp) +; O32-DAG: sb [[R3]], 9([[R1]]) +; NEW-DAG: lw [[R3:\$[0-9]+]], 0($sp) +; NEW-DAG: sb [[R3]], 9([[R1]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 36($sp) +; O32-DAG: sb [[R3]], 10([[R1]]) +; NEW-DAG: lw [[R3:\$[0-9]+]], 8($sp) +; NEW-DAG: sb [[R3]], 10([[R1]]) + +define void @slot_skipping(i8 %a, i64 %b, i8 %c, i8 %d, + i8 %e, i8 %f, i8 %g, i64 %i, i8 %j) nounwind { +entry: + %0 = getelementptr [11 x i8]* @bytes, i32 0, i32 1 + store volatile i8 %a, i8* %0 + %1 = getelementptr [11 x i64]* @dwords, i32 0, i32 1 + store volatile i64 %b, i64* %1 + %2 = getelementptr [11 x i8]* @bytes, i32 0, i32 2 + store volatile i8 %c, i8* %2 + %3 = getelementptr [11 x i8]* @bytes, i32 0, i32 3 + store volatile i8 %d, i8* %3 + %4 = getelementptr [11 x i8]* @bytes, i32 0, i32 4 + store volatile i8 %e, i8* %4 + %5 = getelementptr [11 x i8]* @bytes, i32 0, i32 5 + store volatile i8 %f, i8* %5 + %6 = getelementptr [11 x i8]* @bytes, i32 0, i32 6 + store volatile i8 %g, i8* %6 + %7 = getelementptr [11 x i64]* @dwords, i32 0, i32 2 + store volatile i64 %i, i64* %7 + %8 = getelementptr [11 x i8]* @bytes, i32 0, i32 7 + store volatile i8 %j, i8* %8 + ret void +} + +; ALL-LABEL: slot_skipping: +; We won't test the way the global address is calculated in this test. This is +; just to get the register number for the other checks. +; SYM32-DAG: addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes) +; SYM64-DAG: ld [[R1:\$[0-9]]], %got_disp(bytes)( +; SYM32-DAG: addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(dwords) +; SYM64-DAG: ld [[R2:\$[0-9]]], %got_disp(dwords)( + +; The first argument is the same in O32/N32/N64. +; ALL-DAG: sb $4, 1([[R1]]) + +; The second slot is insufficiently aligned for i64 on O32 so it is skipped. +; Also, i64 occupies two slots on O32 and only one for N32/N64. +; O32-DAG: sw $6, 8([[R2]]) +; O32-DAG: sw $7, 12([[R2]]) +; NEW-DAG: sd $5, 8([[R2]]) + +; N32/N64 get an extra four arguments in registers and still have two left from +; the first four. +; O32 starts loading from the stack. The addresses start at 16 because space is +; always reserved for the first four arguments. +; It's not clear why O32 uses lbu for this argument, but it's not wrong so we'll +; accept it for now. The only IR difference is that this argument has +; anyext from i8 and align 8 on it. +; O32LE-DAG: lbu [[R3:\$[0-9]+]], 16($sp) +; O32BE-DAG: lbu [[R3:\$[0-9]+]], 19($sp) +; O32-DAG: sb [[R3]], 2([[R1]]) +; NEW-DAG: sb $6, 2([[R1]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 20($sp) +; O32-DAG: sb [[R3]], 3([[R1]]) +; NEW-DAG: sb $7, 3([[R1]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 24($sp) +; O32-DAG: sb [[R3]], 4([[R1]]) +; NEW-DAG: sb $8, 4([[R1]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 28($sp) +; O32-DAG: sb [[R3]], 5([[R1]]) +; NEW-DAG: sb $9, 5([[R1]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 32($sp) +; O32-DAG: sb [[R3]], 6([[R1]]) +; NEW-DAG: sb $10, 6([[R1]]) + +; O32-DAG: lw [[R3:\$[0-9]+]], 40($sp) +; O32-DAG: sw [[R3]], 16([[R2]]) +; O32-DAG: lw [[R3:\$[0-9]+]], 44($sp) +; O32-DAG: sw [[R3]], 20([[R2]]) +; NEW-DAG: sd $11, 16([[R2]]) + +; O32/N32/N64 are accessing the stack at this point. +; Unlike O32, N32/N64 do not reserve space for the arguments. +; increase by 4 for O32 and 8 for N32/N64. +; O32-DAG: lw [[R3:\$[0-9]+]], 48($sp) +; O32-DAG: sb [[R3]], 7([[R1]]) +; NEW-DAG: lw [[R3:\$[0-9]+]], 0($sp) +; NEW-DAG: sb [[R3]], 7([[R1]]) Index: test/CodeGen/Mips/cconv/callee-saved-float.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/callee-saved-float.ll @@ -0,0 +1,112 @@ +; RUN: llc -march=mips < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mips < %s | FileCheck --check-prefix=ALL --check-prefix=O32-INV %s +; RUN: llc -march=mipsel < %s | FileCheck --check-prefix=ALL --check-prefix=O32-INV %s + +; RUN-TODO: llc -march=mips64 -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64 -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=ALL-INV --check-prefix=O32-INV %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=ALL-INV --check-prefix=O32-INV %s + +; N32 should match O32 but currently matches N64 +; RUN-TODO: llc -march=mips64 -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN-TODO: llc -march=mips64 -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=ALL-INV --check-prefix=N32-INV %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=ALL-INV --check-prefix=N32-INV %s + +; RUN: llc -march=mips64 -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64 -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=ALL-INV --check-prefix=N64-INV %s +; RUN: llc -march=mips64el -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=ALL-INV --check-prefix=N64-INV %s + +; Test the the callee-saved registers are callee-saved as specified by section +; 2 of the MIPSpro N32 Handbook and section 3 of the SYSV ABI spec. + +define void @fpu_clobber() nounwind { +entry: + call void asm "# Clobber", "~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f12},~{$f13},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31}"() + ret void +} + +; ALL-LABEL: fpu_clobber: +; ALL-INV-NOT: sdc1 $f0, +; ALL-INV-NOT: sdc1 $f1, +; ALL-INV-NOT: sdc1 $f2, +; ALL-INV-NOT: sdc1 $f3, +; ALL-INV-NOT: sdc1 $f4, +; ALL-INV-NOT: sdc1 $f5, +; ALL-INV-NOT: sdc1 $f6, +; ALL-INV-NOT: sdc1 $f7, +; ALL-INV-NOT: sdc1 $f8, +; ALL-INV-NOT: sdc1 $f9, +; ALL-INV-NOT: sdc1 $f10, +; ALL-INV-NOT: sdc1 $f11, +; ALL-INV-NOT: sdc1 $f12, +; ALL-INV-NOT: sdc1 $f13, +; ALL-INV-NOT: sdc1 $f14, +; ALL-INV-NOT: sdc1 $f15, +; ALL-INV-NOT: sdc1 $f16, +; ALL-INV-NOT: sdc1 $f17, +; ALL-INV-NOT: sdc1 $f18, +; ALL-INV-NOT: sdc1 $f19, +; ALL-INV-NOT: sdc1 $f21, +; ALL-INV-NOT: sdc1 $f23, + +; O32: addiu $sp, $sp, -48 +; O32-DAG: sdc1 [[F20:\$f20]], [[OFF20:[0-9]+]]($sp) +; O32-DAG: sdc1 [[F22:\$f22]], [[OFF22:[0-9]+]]($sp) +; O32-DAG: sdc1 [[F24:\$f24]], [[OFF24:[0-9]+]]($sp) +; O32-DAG: sdc1 [[F26:\$f26]], [[OFF26:[0-9]+]]($sp) +; O32-DAG: sdc1 [[F28:\$f28]], [[OFF28:[0-9]+]]($sp) +; O32-DAG: sdc1 [[F30:\$f30]], [[OFF30:[0-9]+]]($sp) +; O32-DAG: ldc1 [[F20]], [[OFF20]]($sp) +; O32-DAG: ldc1 [[F22]], [[OFF22]]($sp) +; O32-DAG: ldc1 [[F24]], [[OFF24]]($sp) +; O32-INV-NOT: sdc1 $f25, +; O32-DAG: ldc1 [[F26]], [[OFF26]]($sp) +; O32-INV-NOT: sdc1 $f27, +; O32-DAG: ldc1 [[F28]], [[OFF28]]($sp) +; O32-INV-NOT: sdc1 $f29, +; O32-DAG: ldc1 [[F30]], [[OFF30]]($sp) +; O32-INV-NOT: sdc1 $f31, +; O32: addiu $sp, $sp, 48 + +; N32: addiu $sp, $sp, -48 +; N32-DAG: sdc1 [[F20:\$f20]], [[OFF20:[0-9]+]]($sp) +; N32-DAG: sdc1 [[F22:\$f22]], [[OFF22:[0-9]+]]($sp) +; N32-DAG: sdc1 [[F24:\$f24]], [[OFF24:[0-9]+]]($sp) +; N32-DAG: sdc1 [[F26:\$f26]], [[OFF26:[0-9]+]]($sp) +; N32-DAG: sdc1 [[F28:\$f28]], [[OFF28:[0-9]+]]($sp) +; N32-DAG: sdc1 [[F30:\$f30]], [[OFF30:[0-9]+]]($sp) +; N32-DAG: ldc1 [[F20]], [[OFF20]]($sp) +; N32-DAG: ldc1 [[F22]], [[OFF22]]($sp) +; N32-DAG: ldc1 [[F24]], [[OFF24]]($sp) +; N32-INV-NOT: sdc1 $f25, +; N32-DAG: ldc1 [[F26]], [[OFF26]]($sp) +; N32-INV-NOT: sdc1 $f27, +; N32-DAG: ldc1 [[F28]], [[OFF28]]($sp) +; N32-INV-NOT: sdc1 $f29, +; N32-DAG: ldc1 [[F30]], [[OFF30]]($sp) +; N32-INV-NOT: sdc1 $f31, +; N32: addiu $sp, $sp, 48 + +; N64: addiu $sp, $sp, -64 +; N64-INV-NOT: sdc1 $f20, +; N64-INV-NOT: sdc1 $f22, +; N64-DAG: sdc1 [[F24:\$f24]], [[OFF24:[0-9]+]]($sp) +; N64-DAG: sdc1 [[F25:\$f25]], [[OFF25:[0-9]+]]($sp) +; N64-DAG: sdc1 [[F26:\$f26]], [[OFF26:[0-9]+]]($sp) +; N64-DAG: sdc1 [[F27:\$f27]], [[OFF27:[0-9]+]]($sp) +; N64-DAG: sdc1 [[F28:\$f28]], [[OFF28:[0-9]+]]($sp) +; N64-DAG: sdc1 [[F29:\$f29]], [[OFF29:[0-9]+]]($sp) +; N64-DAG: sdc1 [[F30:\$f30]], [[OFF30:[0-9]+]]($sp) +; N64-DAG: sdc1 [[F31:\$f31]], [[OFF31:[0-9]+]]($sp) +; N64-DAG: ldc1 [[F24]], [[OFF24]]($sp) +; N64-DAG: ldc1 [[F25]], [[OFF25]]($sp) +; N64-DAG: ldc1 [[F26]], [[OFF26]]($sp) +; N64-DAG: ldc1 [[F27]], [[OFF27]]($sp) +; N64-DAG: ldc1 [[F28]], [[OFF28]]($sp) +; N64-DAG: ldc1 [[F29]], [[OFF29]]($sp) +; N64-DAG: ldc1 [[F30]], [[OFF30]]($sp) +; N64-DAG: ldc1 [[F31]], [[OFF31]]($sp) +; N64: addiu $sp, $sp, 64 Index: test/CodeGen/Mips/cconv/callee-saved.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/callee-saved.ll @@ -0,0 +1,167 @@ +; RUN: llc -march=mips < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mips < %s | FileCheck --check-prefix=ALL --check-prefix=O32-INV %s +; RUN: llc -march=mipsel < %s | FileCheck --check-prefix=ALL --check-prefix=O32-INV %s + +; RUN-TODO: llc -march=mips64 -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64 -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32-INV %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32-INV %s + +; RUN: llc -march=mips64 -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64 -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32-INV %s +; RUN: llc -march=mips64el -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32-INV %s + +; RUN: llc -march=mips64 -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64 -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64-INV %s +; RUN: llc -march=mips64el -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64-INV %s + +; Test the the callee-saved registers are callee-saved as specified by section +; 2 of the MIPSpro N32 Handbook and section 3 of the SYSV ABI spec. + +define void @gpr_clobber() nounwind { +entry: + ; Clobbering the stack pointer is a bad idea so we'll skip that one + call void asm "# Clobber", "~{$0},~{$1},~{$2},~{$3},~{$4},~{$5},~{$6},~{$7},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$16},~{$17},~{$18},~{$19},~{$20},~{$21},~{$22},~{$23},~{$24},~{$25},~{$26},~{$27},~{$28},~{$30},~{$31}"() + ret void +} + +; ALL-LABEL: gpr_clobber: +; O32: addiu $sp, $sp, -40 +; O32-INV-NOT: sw $0, +; O32-INV-NOT: sw $1, +; O32-INV-NOT: sw $2, +; O32-INV-NOT: sw $3, +; O32-INV-NOT: sw $4, +; O32-INV-NOT: sw $5, +; O32-INV-NOT: sw $6, +; O32-INV-NOT: sw $7, +; O32-INV-NOT: sw $8, +; O32-INV-NOT: sw $9, +; O32-INV-NOT: sw $10, +; O32-INV-NOT: sw $11, +; O32-INV-NOT: sw $12, +; O32-INV-NOT: sw $13, +; O32-INV-NOT: sw $14, +; O32-INV-NOT: sw $15, +; O32-DAG: sw [[G16:\$16]], [[OFF16:[0-9]+]]($sp) +; O32-DAG: sw [[G17:\$17]], [[OFF17:[0-9]+]]($sp) +; O32-DAG: sw [[G18:\$18]], [[OFF18:[0-9]+]]($sp) +; O32-DAG: sw [[G19:\$19]], [[OFF19:[0-9]+]]($sp) +; O32-DAG: sw [[G20:\$20]], [[OFF20:[0-9]+]]($sp) +; O32-DAG: sw [[G21:\$21]], [[OFF21:[0-9]+]]($sp) +; O32-DAG: sw [[G22:\$22]], [[OFF22:[0-9]+]]($sp) +; O32-DAG: sw [[G23:\$23]], [[OFF23:[0-9]+]]($sp) +; O32-INV-NOT: sw $24, +; O32-INV-NOT: sw $25, +; O32-INV-NOT: sw $26, +; O32-INV-NOT: sw $27, +; O32-INV-NOT: sw $28, +; O32-INV-NOT: sw $29, +; O32-DAG: sw [[G30:\$fp]], [[OFF30:[0-9]+]]($sp) +; O32-DAG: sw [[G31:\$fp]], [[OFF31:[0-9]+]]($sp) +; O32-DAG: lw [[G16]], [[OFF16]]($sp) +; O32-DAG: lw [[G17]], [[OFF17]]($sp) +; O32-DAG: lw [[G18]], [[OFF18]]($sp) +; O32-DAG: lw [[G19]], [[OFF19]]($sp) +; O32-DAG: lw [[G20]], [[OFF20]]($sp) +; O32-DAG: lw [[G21]], [[OFF21]]($sp) +; O32-DAG: lw [[G22]], [[OFF22]]($sp) +; O32-DAG: lw [[G23]], [[OFF23]]($sp) +; O32-DAG: lw [[G30]], [[OFF30]]($sp) +; O32-DAG: lw [[G31]], [[OFF31]]($sp) +; O32: addiu $sp, $sp, 40 + +; N32: addiu $sp, $sp, -96 +; N32-INV-NOT: sd $0, +; N32-INV-NOT: sd $1, +; N32-INV-NOT: sd $2, +; N32-INV-NOT: sd $3, +; N32-INV-NOT: sd $4, +; N32-INV-NOT: sd $5, +; N32-INV-NOT: sd $6, +; N32-INV-NOT: sd $7, +; N32-INV-NOT: sd $8, +; N32-INV-NOT: sd $9, +; N32-INV-NOT: sd $10, +; N32-INV-NOT: sd $11, +; N32-INV-NOT: sd $12, +; N32-INV-NOT: sd $13, +; N32-INV-NOT: sd $14, +; N32-INV-NOT: sd $15, +; N32-DAG: sd [[G16:\$16]], [[OFF16:[0-9]+]]($sp) +; N32-DAG: sd [[G17:\$17]], [[OFF17:[0-9]+]]($sp) +; N32-DAG: sd [[G18:\$18]], [[OFF18:[0-9]+]]($sp) +; N32-DAG: sd [[G19:\$19]], [[OFF19:[0-9]+]]($sp) +; N32-DAG: sd [[G20:\$20]], [[OFF20:[0-9]+]]($sp) +; N32-DAG: sd [[G21:\$21]], [[OFF21:[0-9]+]]($sp) +; N32-DAG: sd [[G22:\$22]], [[OFF22:[0-9]+]]($sp) +; N32-DAG: sd [[G23:\$23]], [[OFF23:[0-9]+]]($sp) +; N32-INV-NOT: sd $24, +; N32-INV-NOT: sd $25, +; N32-INV-NOT: sd $26, +; N32-INV-NOT: sd $27, +; N32-DAG: sd [[G28:\$gp]], [[OFF28:[0-9]+]]($sp) +; N32-INV-NOT: sd $29, +; N32-DAG: sd [[G30:\$fp]], [[OFF30:[0-9]+]]($sp) +; N32-DAG: sd [[G31:\$fp]], [[OFF31:[0-9]+]]($sp) +; N32-DAG: ld [[G16]], [[OFF16]]($sp) +; N32-DAG: ld [[G17]], [[OFF17]]($sp) +; N32-DAG: ld [[G18]], [[OFF18]]($sp) +; N32-DAG: ld [[G19]], [[OFF19]]($sp) +; N32-DAG: ld [[G20]], [[OFF20]]($sp) +; N32-DAG: ld [[G21]], [[OFF21]]($sp) +; N32-DAG: ld [[G22]], [[OFF22]]($sp) +; N32-DAG: ld [[G23]], [[OFF23]]($sp) +; N32-DAG: ld [[G28]], [[OFF28]]($sp) +; N32-DAG: ld [[G30]], [[OFF30]]($sp) +; N32-DAG: ld [[G31]], [[OFF31]]($sp) +; N32: addiu $sp, $sp, 96 + +; N64: daddiu $sp, $sp, -96 +; N64-INV-NOT: sd $0, +; N64-INV-NOT: sd $1, +; N64-INV-NOT: sd $2, +; N64-INV-NOT: sd $3, +; N64-INV-NOT: sd $4, +; N64-INV-NOT: sd $5, +; N64-INV-NOT: sd $6, +; N64-INV-NOT: sd $7, +; N64-INV-NOT: sd $8, +; N64-INV-NOT: sd $9, +; N64-INV-NOT: sd $10, +; N64-INV-NOT: sd $11, +; N64-INV-NOT: sd $12, +; N64-INV-NOT: sd $13, +; N64-INV-NOT: sd $14, +; N64-INV-NOT: sd $15, +; N64-DAG: sd [[G16:\$16]], [[OFF16:[0-9]+]]($sp) +; N64-DAG: sd [[G17:\$17]], [[OFF17:[0-9]+]]($sp) +; N64-DAG: sd [[G18:\$18]], [[OFF18:[0-9]+]]($sp) +; N64-DAG: sd [[G19:\$19]], [[OFF19:[0-9]+]]($sp) +; N64-DAG: sd [[G20:\$20]], [[OFF20:[0-9]+]]($sp) +; N64-DAG: sd [[G21:\$21]], [[OFF21:[0-9]+]]($sp) +; N64-DAG: sd [[G22:\$22]], [[OFF22:[0-9]+]]($sp) +; N64-DAG: sd [[G23:\$23]], [[OFF23:[0-9]+]]($sp) +; N64-DAG: sd [[G30:\$fp]], [[OFF30:[0-9]+]]($sp) +; N64-DAG: sd [[G31:\$fp]], [[OFF31:[0-9]+]]($sp) +; N64-INV-NOT: sd $24, +; N64-INV-NOT: sd $25, +; N64-INV-NOT: sd $26, +; N64-INV-NOT: sd $27, +; N64-DAG: sd [[G28:\$gp]], [[OFF28:[0-9]+]]($sp) +; N64-INV-NOT: sd $29, +; N64-DAG: ld [[G16]], [[OFF16]]($sp) +; N64-DAG: ld [[G17]], [[OFF17]]($sp) +; N64-DAG: ld [[G18]], [[OFF18]]($sp) +; N64-DAG: ld [[G19]], [[OFF19]]($sp) +; N64-DAG: ld [[G20]], [[OFF20]]($sp) +; N64-DAG: ld [[G21]], [[OFF21]]($sp) +; N64-DAG: ld [[G22]], [[OFF22]]($sp) +; N64-DAG: ld [[G23]], [[OFF23]]($sp) +; N64-DAG: ld [[G28]], [[OFF28]]($sp) +; N64-DAG: ld [[G30]], [[OFF30]]($sp) +; N64-DAG: ld [[G31]], [[OFF31]]($sp) +; N64: daddiu $sp, $sp, 96 Index: test/CodeGen/Mips/cconv/memory-layout.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/memory-layout.ll @@ -0,0 +1,140 @@ +; RUN: llc -march=mips < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN-TODO: llc -march=mips64 -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN: llc -march=mips64 -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s + +; RUN: llc -march=mips64 -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s + +; Test the memory layout for all ABI's and byte orders as specified by section +; 4 of MD00305 (MIPS ABIs Described). +; Bitfields are not covered since they are not available as a type in LLVM IR. +; +; The assembly directives deal with endianness so we don't need to account for +; that. + +; Deliberately request alignments that are too small for the target so we get +; the minimum alignment instead of the preferred alignment. +@byte = global i8 1, align 1 +@halfword = global i16 258, align 1 +@word = global i32 16909060, align 1 +@float = global float 1.0, align 1 +@dword = global i64 283686952306183, align 1 +@double = global double 1.0, align 1 +@pointer = global i8* @byte + +; ALL-NOT: .align +; ALL-LABEL: byte: +; ALL: .byte 1 +; ALL: .size byte, 1 + +; ALL: .align 1 +; ALL-LABEL: halfword: +; ALL: .2byte 258 +; ALL: .size halfword, 2 + +; ALL: .align 2 +; ALL-LABEL: word: +; ALL: .4byte 16909060 +; ALL: .size word, 4 + +; ALL: .align 2 +; ALL-LABEL: float: +; ALL: .4byte 1065353216 +; ALL: .size float, 4 + +; ALL: .align 3 +; ALL-LABEL: dword: +; ALL: .8byte 283686952306183 +; ALL: .size dword, 8 + +; ALL: .align 3 +; ALL-LABEL: double: +; ALL: .8byte 4607182418800017408 +; ALL: .size double, 8 + +; O32: .align 2 +; N32: .align 2 +; N64: .align 3 +; ALL-LABEL: pointer: +; O32: .4byte byte +; O32: .size pointer, 4 +; N32: .4byte byte +; N32: .size pointer, 4 +; N64: .8byte byte +; N64: .size pointer, 8 + +@byte_array = global [2 x i8] [i8 1, i8 2], align 1 +@halfword_array = global [2 x i16] [i16 1, i16 2], align 1 +@word_array = global [2 x i32] [i32 1, i32 2], align 1 +@float_array = global [2 x float] [float 1.0, float 2.0], align 1 +@dword_array = global [2 x i64] [i64 1, i64 2], align 1 +@double_array = global [2 x double] [double 1.0, double 2.0], align 1 +@pointer_array = global [2 x i8*] [i8* @byte, i8* @byte] + +; ALL-NOT: .align +; ALL-LABEL: byte_array: +; ALL: .ascii "\001\002" +; ALL: .size byte_array, 2 + +; ALL: .align 1 +; ALL-LABEL: halfword_array: +; ALL: .2byte 1 +; ALL: .2byte 2 +; ALL: .size halfword_array, 4 + +; ALL: .align 2 +; ALL-LABEL: word_array: +; ALL: .4byte 1 +; ALL: .4byte 2 +; ALL: .size word_array, 8 + +; ALL: .align 2 +; ALL-LABEL: float_array: +; ALL: .4byte 1065353216 +; ALL: .4byte 1073741824 +; ALL: .size float_array, 8 + +; ALL: .align 3 +; ALL-LABEL: dword_array: +; ALL: .8byte 1 +; ALL: .8byte 2 +; ALL: .size dword_array, 16 + +; ALL: .align 3 +; ALL-LABEL: double_array: +; ALL: .8byte 4607182418800017408 +; ALL: .8byte 4611686018427387904 +; ALL: .size double_array, 16 + +; O32: .align 2 +; N32: .align 2 +; N64: .align 3 +; ALL-LABEL: pointer_array: +; O32: .4byte byte +; O32: .4byte byte +; O32: .size pointer_array, 8 +; N32: .4byte byte +; N32: .4byte byte +; N32: .size pointer_array, 8 +; N64: .8byte byte +; N64: .8byte byte +; N64: .size pointer_array, 16 + +%mixed = type { i8, double, i16 } +@mixed = global %mixed { i8 1, double 1.0, i16 515 }, align 1 + +; ALL: .align 3 +; ALL-LABEL: mixed: +; ALL: .byte 1 +; ALL: .space 7 +; ALL: .8byte 4607182418800017408 +; ALL: .2byte 515 +; ALL: .space 6 +; ALL: .size mixed, 24 + +; Bitfields are not available in LLVM IR so we can't test them here. Index: test/CodeGen/Mips/cconv/reserved-space.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/reserved-space.ll @@ -0,0 +1,39 @@ +; RUN: llc -march=mips < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN-TODO: llc -march=mips64 -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN: llc -march=mips64 -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s + +; RUN: llc -march=mips64 -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s + +; Test that O32 correctly reserved space for the four arguments, even when +; there aren't any as per section 5 of MD00305 (MIPS ABIs Described). + +declare void @foo() nounwind; + +define void @reserved_space() nounwind { +entry: + tail call void @foo() + ret void +} + +; ALL-LABEL: reserved_space: +; O32: addiu $sp, $sp, -24 +; O32: sw $ra, 20($sp) +; O32: lw $ra, 20($sp) +; O32: addiu $sp, $sp, 24 +; Despite pointers being 32-bit wide on N32, the return pointer is saved as a +; 64-bit pointer. I've yet to find a documentation reference for this quirk but +; this behaviour matches GCC so I have considered it to be correct. +; N32: addiu $sp, $sp, -16 +; N32: sd $ra, 8($sp) +; N32: ld $ra, 8($sp) +; N32: addiu $sp, $sp, 16 +; N64: daddiu $sp, $sp, -16 +; N64: sd $ra, 8($sp) +; N64: ld $ra, 8($sp) +; N64: daddiu $sp, $sp, 16 Index: test/CodeGen/Mips/cconv/return-float.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/return-float.ll @@ -0,0 +1,45 @@ +; RUN: llc -march=mips -soft-float -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel -soft-float -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN-TODO: llc -march=mips64 -soft-float -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -soft-float -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN: llc -march=mips64 -soft-float -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -soft-float -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s + +; RUN: llc -march=mips64 -soft-float -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -soft-float -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s + +; Test the float returns for all ABI's and byte orders as specified by +; section 5 of MD00305 (MIPS ABIs Described). + +@float = global float zeroinitializer +@double = global double zeroinitializer + +define float @retfloat() nounwind { +entry: + %0 = load volatile float* @float + ret float %0 +} + +; ALL-LABEL: retfloat: +; O32-DAG: lui [[R1:\$[0-9]+]], %hi(float) +; O32-DAG: lw $2, %lo(float)([[R1]]) +; N32-DAG: lui [[R1:\$[0-9]+]], %hi(float) +; N32-DAG: lw $2, %lo(float)([[R1]]) +; N64-DAG: ld [[R1:\$[0-9]+]], %got_disp(float)($1) +; N64-DAG: lw $2, 0([[R1]]) + +define double @retdouble() nounwind { +entry: + %0 = load volatile double* @double + ret double %0 +} + +; ALL-LABEL: retdouble: +; O32-DAG: lw $2, %lo(double)([[R1:\$[0-9]+]]) +; O32-DAG: addiu [[R2:\$[0-9]+]], [[R1]], %lo(double) +; O32-DAG: lw $3, 4([[R2]]) +; N32-DAG: ld $2, %lo(double)([[R1:\$[0-9]+]]) +; N64-DAG: ld [[R1:\$[0-9]+]], %got_disp(double)($1) +; N64-DAG: ld $2, 0([[R1]]) Index: test/CodeGen/Mips/cconv/return-hard-float.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/return-hard-float.ll @@ -0,0 +1,43 @@ +; RUN: llc -march=mips -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN-TODO: llc -march=mips64 -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s + +; Test the float returns for all ABI's and byte orders as specified by +; section 5 of MD00305 (MIPS ABIs Described). + +@float = global float zeroinitializer +@double = global double zeroinitializer + +define float @retfloat() nounwind { +entry: + %0 = load volatile float* @float + ret float %0 +} + +; ALL-LABEL: retfloat: +; O32-DAG: lui [[R1:\$[0-9]+]], %hi(float) +; O32-DAG: lwc1 $f0, %lo(float)([[R1]]) +; N32-DAG: lui [[R1:\$[0-9]+]], %hi(float) +; N32-DAG: lwc1 $f0, %lo(float)([[R1]]) +; N64-DAG: ld [[R1:\$[0-9]+]], %got_disp(float)($1) +; N64-DAG: lwc1 $f0, 0([[R1]]) + +define double @retdouble() nounwind { +entry: + %0 = load volatile double* @double + ret double %0 +} + +; ALL-LABEL: retdouble: +; O32-DAG: ldc1 $f0, %lo(double)([[R1:\$[0-9]+]]) +; N32-DAG: ldc1 $f0, %lo(double)([[R1:\$[0-9]+]]) +; N64-DAG: ld [[R1:\$[0-9]+]], %got_disp(double)($1) +; N64-DAG: ldc1 $f0, 0([[R1]]) Index: test/CodeGen/Mips/cconv/return-hard-fp128.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/return-hard-fp128.ll @@ -0,0 +1,31 @@ +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s + +; Test the fp128 returns for N32/N64 and all byte orders as specified by +; section 5 of MD00305 (MIPS ABIs Described). +; +; O32 is not tested because long double is the same as double on O32. +; +@fp128 = global fp128 zeroinitializer + +define fp128 @retldouble() nounwind { +entry: + %0 = load volatile fp128* @fp128 + ret fp128 %0 +} + +; ALL-LABEL: retldouble: +; N32-DAG: ld [[R2:\$[0-9]+]], %lo(fp128)([[R1:\$[0-9]+]]) +; N32-DAG: addiu [[R3:\$[0-9]+]], [[R1]], %lo(fp128) +; N32-DAG: ld [[R4:\$[0-9]+]], 8([[R3]]) +; N32-DAG: dmtc1 [[R2]], $f0 +; N32-DAG: dmtc1 [[R4]], $f2 + +; N64-DAG: ld [[R2:\$[0-9]+]], %got_disp(fp128)([[R1:\$[0-9]+]]) +; N64-DAG: ld [[R3:\$[0-9]+]], 0([[R2]]) +; N64-DAG: ld [[R4:\$[0-9]+]], 8([[R2]]) +; N64-DAG: dmtc1 [[R3]], $f0 +; N64-DAG: dmtc1 [[R4]], $f2 Index: test/CodeGen/Mips/cconv/return.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/return.ll @@ -0,0 +1,63 @@ +; RUN: llc -march=mips -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel -relocation-model=static < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN-TODO: llc -march=mips64 -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -relocation-model=static -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s + +; RUN: llc -march=mips64 -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -relocation-model=static -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s + +; Test the integer returns for all ABI's and byte orders as specified by +; section 5 of MD00305 (MIPS ABIs Described). +; +@byte = global i8 zeroinitializer +@word = global i32 zeroinitializer +@dword = global i64 zeroinitializer +@float = global float zeroinitializer +@double = global double zeroinitializer + +define i8 @reti8() nounwind { +entry: + %0 = load volatile i8* @byte + ret i8 %0 +} + +; ALL-LABEL: reti8: +; O32-DAG: lui [[R1:\$[0-9]+]], %hi(byte) +; O32-DAG: lbu $2, %lo(byte)([[R1]]) +; N32-DAG: lui [[R1:\$[0-9]+]], %hi(byte) +; N32-DAG: lbu $2, %lo(byte)([[R1]]) +; N64-DAG: ld [[R1:\$[0-9]+]], %got_disp(byte)($1) +; N64-DAG: lbu $2, 0([[R1]]) + +define i32 @reti32() nounwind { +entry: + %0 = load volatile i32* @word + ret i32 %0 +} + +; ALL-LABEL: reti32: +; O32-DAG: lui [[R1:\$[0-9]+]], %hi(word) +; O32-DAG: lw $2, %lo(word)([[R1]]) +; N32-DAG: lui [[R1:\$[0-9]+]], %hi(word) +; N32-DAG: lw $2, %lo(word)([[R1]]) +; N64-DAG: ld [[R1:\$[0-9]+]], %got_disp(word)($1) +; N64-DAG: lw $2, 0([[R1]]) + +define i64 @reti64() nounwind { +entry: + %0 = load volatile i64* @dword + ret i64 %0 +} + +; ALL-LABEL: reti64: +; On O32, we must use v0 and v1 for the return value +; O32-DAG: lw $2, %lo(dword)([[R1:\$[0-9]+]]) +; O32-DAG: addiu [[R2:\$[0-9]+]], [[R1]], %lo(dword) +; O32-DAG: lw $3, 4([[R2]]) +; N32-DAG: ld $2, %lo(dword)([[R1:\$[0-9]+]]) +; N64-DAG: ld [[R1:\$[0-9]+]], %got_disp(dword)([[R1:\$[0-9]+]]) +; N64-DAG: ld $2, 0([[R1]]) Index: test/CodeGen/Mips/cconv/stack-alignment.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/cconv/stack-alignment.ll @@ -0,0 +1,28 @@ +; RUN: llc -march=mips < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN: llc -march=mipsel < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN-TODO: llc -march=mips64 -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s +; RUN-TODO: llc -march=mips64el -mattr=-n64,+o32 < %s | FileCheck --check-prefix=ALL --check-prefix=O32 %s + +; RUN: llc -march=mips64 -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s +; RUN: llc -march=mips64el -mattr=-n64,+n32 < %s | FileCheck --check-prefix=ALL --check-prefix=N32 %s + +; RUN: llc -march=mips64 -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s +; RUN: llc -march=mips64el -mattr=-n64,+n64 < %s | FileCheck --check-prefix=ALL --check-prefix=N64 %s + +; Test the stack alignment for all ABI's and byte orders as specified by +; section 5 of MD00305 (MIPS ABIs Described). + +define void @local_bytes_1() nounwind { +entry: + %0 = alloca i8 + ret void +} + +; ALL-LABEL: local_bytes_1: +; O32: addiu $sp, $sp, -8 +; O32: addiu $sp, $sp, 8 +; N32: addiu $sp, $sp, -16 +; N32: addiu $sp, $sp, 16 +; N64: addiu $sp, $sp, -16 +; N64: addiu $sp, $sp, 16