Add a register bank for floating point values and select simple instructions
using them (add, copies from GPR).
This assumes that the hardware can cope with a single precision add (VADDS)
instruction, so the legalizer will treat G_FADD as legal and the instruction
selector will refuse to select if the hardware doesn't support it. In the future
we'll want to be more careful about this, and legalize to libcalls if we have to
use soft float.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@294442
91177308-0d34-0410-b5e6-
96231b3b80d8
DstSize <= SrcSize)) &&
"Copy with different width?!");
- assert(RegBank->getID() == ARM::GPRRegBankID && "Unsupported reg bank");
+ assert((RegBank->getID() == ARM::GPRRegBankID ||
+ RegBank->getID() == ARM::FPRRegBankID) &&
+ "Unsupported reg bank");
+
const TargetRegisterClass *RC = &ARM::GPRRegClass;
+ if (RegBank->getID() == ARM::FPRRegBankID) {
+ assert(DstSize == 32 && "Only 32-bit FP values are supported");
+ RC = &ARM::SPRRegClass;
+ }
+
// No need to constrain SrcReg. It will get constrained when
// we hit another of its uses or its defs.
// Copies do not have constraints.
I.setDesc(TII.get(ARM::ADDrr));
MIB.add(predOps(ARMCC::AL)).add(condCodeOp());
break;
+ case G_FADD:
+ if (!TII.getSubtarget().hasVFP2() ||
+ TII.getSubtarget().useNEONForSinglePrecisionFP())
+ return false;
+ I.setDesc(TII.get(ARM::VADDS));
+ MIB.add(predOps(ARMCC::AL));
+ break;
case G_FRAME_INDEX:
// Add 0 to the given frame index and hope it will eventually be folded into
// the user(s).
setAction({Op, 1, Ty}, Legal);
}
+ // FIXME: This is a bit sloppy, but for now we'll just rely on the instruction
+ // selector to complain if it doesn't support floating point.
+ setAction({G_FADD, s32}, Legal);
+
computeTables();
}
namespace llvm {
namespace ARM {
RegisterBankInfo::PartialMapping GPRPartialMapping{0, 32, GPRRegBank};
+RegisterBankInfo::PartialMapping FPRPartialMapping{0, 32, FPRRegBank};
RegisterBankInfo::ValueMapping ValueMappings[] = {
- {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}};
+ {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1},
+ {&FPRPartialMapping, 1}, {&FPRPartialMapping, 1}, {&FPRPartialMapping, 1}};
} // end namespace arm
} // end namespace llvm
case GPRnopcRegClassID:
case tGPR_and_tcGPRRegClassID:
return getRegBank(ARM::GPRRegBankID);
+ case SPR_8RegClassID:
+ case SPRRegClassID:
+ return getRegBank(ARM::FPRRegBankID);
default:
llvm_unreachable("Unsupported register kind");
}
// the real world we would use different mappings.
OperandsMapping = &ARM::ValueMappings[0];
break;
+ case G_FADD:
+ OperandsMapping = &ARM::ValueMappings[3];
+ break;
case G_FRAME_INDEX:
OperandsMapping = getOperandsMapping({&ARM::ValueMappings[0], nullptr});
break;
//===----------------------------------------------------------------------===//
def GPRRegBank : RegisterBank<"GPRB", [GPR, GPRwithAPSR]>;
+def FPRRegBank : RegisterBank<"FPRB", [SPR]>;
define void @test_add_s16() { ret void }
define void @test_add_s32() { ret void }
+ define void @test_fadd_s32() #0 { ret void }
+
define void @test_load_from_stack() { ret void }
+
+ attributes #0 = { "target-features"="+vfp2" }
...
---
name: test_zext_s1
; CHECK: BX_RET 14, _, implicit %r0
...
---
+name: test_fadd_s32
+# CHECK-LABEL: name: test_fadd_s32
+legalized: true
+regBankSelected: true
+selected: false
+# CHECK: selected: true
+registers:
+ - { id: 0, class: fprb }
+ - { id: 1, class: fprb }
+ - { id: 2, class: fprb }
+# CHECK: id: 0, class: spr
+# CHECK: id: 1, class: spr
+# CHECK: id: 2, class: spr
+body: |
+ bb.0:
+ liveins: %s0, %s1
+
+ %0(s32) = COPY %s0
+ ; CHECK: [[VREGX:%[0-9]+]] = COPY %s0
+
+ %1(s32) = COPY %s1
+ ; CHECK: [[VREGY:%[0-9]+]] = COPY %s1
+
+ %2(s32) = G_FADD %0, %1
+ ; CHECK: [[VREGSUM:%[0-9]+]] = VADDS [[VREGX]], [[VREGY]], 14, _
+
+ %s0 = COPY %2(s32)
+ ; CHECK: %s0 = COPY [[VREGSUM]]
+
+ BX_RET 14, _, implicit %s0
+ ; CHECK: BX_RET 14, _, implicit %s0
+...
+---
name: test_load_from_stack
# CHECK-LABEL: name: test_load_from_stack
legalized: true
define void @test_load_from_stack() { ret void }
define void @test_legal_loads() { ret void }
+
+ define void @test_fadd_s32() { ret void }
...
---
name: test_sext_s8
%5(p0) = G_LOAD %0(p0)
BX_RET 14, _
...
+---
+name: test_fadd_s32
+# CHECK-LABEL: name: test_fadd_s32
+legalized: false
+# CHECK: legalized: true
+regBankSelected: false
+selected: false
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: _ }
+ - { id: 1, class: _ }
+ - { id: 2, class: _ }
+body: |
+ bb.0:
+ liveins: %r0, %r1
+
+ %0(s32) = COPY %r0
+ %1(s32) = COPY %r1
+ %2(s32) = G_FADD %0, %1
+ ; G_FADD with s32 is legal, so we should find it unchanged in the output
+ ; CHECK: {{%[0-9]+}}(s32) = G_FADD {{%[0-9]+, %[0-9]+}}
+ %r0 = COPY %2(s32)
+ BX_RET 14, _, implicit %r0
+
+...
define void @test_add_s1() { ret void }
define void @test_loads() { ret void }
+
+ define void @test_fadd_s32() { ret void }
...
---
name: test_add_s32
BX_RET 14, _, implicit %r0
...
+---
+name: test_fadd_s32
+# CHECK-LABEL: name: test_fadd_s32
+legalized: true
+regBankSelected: false
+selected: false
+# CHECK: registers:
+# CHECK: - { id: 0, class: fprb }
+# CHECK: - { id: 1, class: fprb }
+# CHECK: - { id: 2, class: fprb }
+
+registers:
+ - { id: 0, class: _ }
+ - { id: 1, class: _ }
+ - { id: 2, class: _ }
+body: |
+ bb.0:
+ liveins: %r0, %r1
+
+ %0(s32) = COPY %s0
+ %1(s32) = COPY %s1
+ %2(s32) = G_FADD %0, %1
+ %s0 = COPY %2(s32)
+ BX_RET 14, _, implicit %r0
+
+...