From 5eb9f581b664e0e35c6bf6db4f696f57a8516523 Mon Sep 17 00:00:00 2001 From: Daniel Sanders Date: Sat, 28 Apr 2018 18:14:50 +0000 Subject: [PATCH] [globalisel][legalizerinfo] Introduce dedicated extending loads and add lowerings for them Summary: Previously, a extending load was represented at (G_*EXT (G_LOAD x)). This had a few drawbacks: * G_LOAD had to be legal for all sizes you could extend from, even if registers didn't naturally hold those sizes. * All sizes you could extend from had to be allocatable just in case the extend went missing (e.g. by optimization). * At minimum, G_*EXT and G_TRUNC had to be legal for these sizes. As we improve optimization of extends and truncates, this legality requirement would spread without considerable care w.r.t when certain combines were permitted. * The SelectionDAG importer required some ugly and fragile pattern rewriting to translate patterns into this style. This patch begins changing the representation to: * (G_[SZ]EXTLOAD x) * (G_LOAD x) any-extends when MMO.getSize() * 8 < ResultTy.getSizeInBits() which resolves these issues by allowing targets to work entirely in their native register sizes, and by having a more direct translation from SelectionDAG patterns. This patch introduces the new generic instructions and new variation on G_LOAD and adds lowering for them to convert back to the existing representations. Depends on D45466 Reviewers: ab, aditya_nandakumar, bogner, rtereshin, volkan, rovka, aemerson, javed.absar Reviewed By: aemerson Subscribers: aemerson, kristof.beyls, javed.absar, llvm-commits Differential Revision: https://reviews.llvm.org/D45540 llvm-svn: 331115 --- .../CodeGen/GlobalISel/MachineIRBuilder.h | 12 +++++ llvm/include/llvm/Support/TargetOpcodes.def | 8 +++- llvm/include/llvm/Target/GenericOpcodes.td | 16 +++++++ .../CodeGen/GlobalISel/LegalizerHelper.cpp | 48 +++++++++++++++++-- .../CodeGen/GlobalISel/MachineIRBuilder.cpp | 8 +++- .../Target/AArch64/AArch64LegalizerInfo.cpp | 6 +++ .../AArch64/GlobalISel/legalize-extload.mir | 25 ++++++++++ .../AArch64/GlobalISel/legalize-sextload.mir | 25 ++++++++++ .../AArch64/GlobalISel/legalize-zextload.mir | 25 ++++++++++ 9 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/legalize-extload.mir create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/legalize-sextload.mir create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/legalize-zextload.mir diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h index 6b6ce4a20098c..08021706fe71d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -521,6 +521,18 @@ class MachineIRBuilderBase { MachineInstrBuilder buildLoad(unsigned Res, unsigned Addr, MachineMemOperand &MMO); + /// Build and insert `Res = Addr, MMO`. + /// + /// Loads the value stored at \p Addr. Puts the result in \p Res. + /// + /// \pre setBasicBlock or setMI must have been called. + /// \pre \p Res must be a generic virtual register. + /// \pre \p Addr must be a generic virtual register with pointer type. + /// + /// \return a MachineInstrBuilder for the newly created instruction. + MachineInstrBuilder buildLoadInstr(unsigned Opcode, unsigned Res, + unsigned Addr, MachineMemOperand &MMO); + /// Build and insert `G_STORE Val, Addr, MMO`. /// /// Stores the value \p Val to \p Addr. diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def index 8ab7348ca6d4c..1606e7653bad5 100644 --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -265,9 +265,15 @@ HANDLE_TARGET_OPCODE(G_INTTOPTR) /// COPY is the relevant instruction. HANDLE_TARGET_OPCODE(G_BITCAST) -/// Generic load. +/// Generic load (including anyext load) HANDLE_TARGET_OPCODE(G_LOAD) +/// Generic signext load +HANDLE_TARGET_OPCODE(G_SEXTLOAD) + +/// Generic zeroext load +HANDLE_TARGET_OPCODE(G_ZEXTLOAD) + /// Generic store. HANDLE_TARGET_OPCODE(G_STORE) diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td index 1af6a9ab9f257..89516b3ee7588 100644 --- a/llvm/include/llvm/Target/GenericOpcodes.td +++ b/llvm/include/llvm/Target/GenericOpcodes.td @@ -482,6 +482,22 @@ def G_LOAD : GenericInstruction { let mayLoad = 1; } +// Generic sign-extended load. Expects a MachineMemOperand in addition to explicit operands. +def G_SEXTLOAD : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins ptype1:$addr); + let hasSideEffects = 0; + let mayLoad = 1; +} + +// Generic zero-extended load. Expects a MachineMemOperand in addition to explicit operands. +def G_ZEXTLOAD : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins ptype1:$addr); + let hasSideEffects = 0; + let mayLoad = 1; +} + // Generic store. Expects a MachineMemOperand in addition to explicit operands. def G_STORE : GenericInstruction { let OutOperandList = (outs); diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp index 60901e09f0acb..e4e5e0c602256 100644 --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -732,17 +732,19 @@ LegalizerHelper::widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy) { MI.eraseFromParent(); return Legalized; } - case TargetOpcode::G_LOAD: { + case TargetOpcode::G_LOAD: // For some types like i24, we might try to widen to i32. To properly handle // this we should be using a dedicated extending load, until then avoid // trying to legalize. if (alignTo(MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(), 8) != WideTy.getSizeInBits()) return UnableToLegalize; - + LLVM_FALLTHROUGH; + case TargetOpcode::G_SEXTLOAD: + case TargetOpcode::G_ZEXTLOAD: { unsigned DstExt = MRI.createGenericVirtualRegister(WideTy); - MIRBuilder.buildLoad(DstExt, MI.getOperand(1).getReg(), - **MI.memoperands_begin()); + MIRBuilder.buildLoadInstr(MI.getOpcode(), DstExt, MI.getOperand(1).getReg(), + **MI.memoperands_begin()); MIRBuilder.buildTrunc(MI.getOperand(0).getReg(), DstExt); MI.eraseFromParent(); return Legalized; @@ -1030,6 +1032,44 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { MI.eraseFromParent(); return Legalized; } + case TargetOpcode::G_LOAD: + case TargetOpcode::G_SEXTLOAD: + case TargetOpcode::G_ZEXTLOAD: { + // Lower to a memory-width G_LOAD and a G_SEXT/G_ZEXT/G_ANYEXT + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned PtrReg = MI.getOperand(1).getReg(); + LLT DstTy = MRI.getType(DstReg); + auto &MMO = **MI.memoperands_begin(); + + if (DstTy.getSizeInBits() == MMO.getSize() /* in bytes */ * 8) { + MIRBuilder.buildLoad(DstReg, PtrReg, MMO); + MI.eraseFromParent(); + return Legalized; + } + + if (DstTy.isScalar()) { + unsigned TmpReg = MRI.createGenericVirtualRegister( + LLT::scalar(MMO.getSize() /* in bytes */ * 8)); + MIRBuilder.buildLoad(TmpReg, PtrReg, MMO); + switch (MI.getOpcode()) { + default: + llvm_unreachable("Unexpected opcode"); + case TargetOpcode::G_LOAD: + MIRBuilder.buildAnyExt(DstReg, TmpReg); + break; + case TargetOpcode::G_SEXTLOAD: + MIRBuilder.buildSExt(DstReg, TmpReg); + break; + case TargetOpcode::G_ZEXTLOAD: + MIRBuilder.buildZExt(DstReg, TmpReg); + break; + } + MI.eraseFromParent(); + return Legalized; + } + + return UnableToLegalize; + } } } diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp index 96d95673914c1..273044bad757d 100644 --- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -278,10 +278,16 @@ MachineInstrBuilder MachineIRBuilderBase::buildBrCond(unsigned Tst, MachineInstrBuilder MachineIRBuilderBase::buildLoad(unsigned Res, unsigned Addr, MachineMemOperand &MMO) { + return buildLoadInstr(TargetOpcode::G_LOAD, Res, Addr, MMO); +} + +MachineInstrBuilder +MachineIRBuilderBase::buildLoadInstr(unsigned Opcode, unsigned Res, + unsigned Addr, MachineMemOperand &MMO) { assert(getMRI()->getType(Res).isValid() && "invalid operand type"); assert(getMRI()->getType(Addr).isPointer() && "invalid operand type"); - return buildInstr(TargetOpcode::G_LOAD) + return buildInstr(Opcode) .addDef(Res) .addUse(Addr) .addMemOperand(&MMO); diff --git a/llvm/lib/Target/AArch64/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/AArch64LegalizerInfo.cpp index 0742c76e8f3fc..1a1f9adf46f14 100644 --- a/llvm/lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -135,6 +135,9 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) { .maxScalarIf(typeInSet(1, {s64}), 0, s32) .widenScalarToNextPow2(0); + getActionDefinitionsBuilder({G_SEXTLOAD, G_ZEXTLOAD}) + .lower(); + getActionDefinitionsBuilder({G_LOAD, G_STORE}) .legalForTypesWithMemSize({{s8, p0, 8}, {s16, p0, 16}, @@ -147,6 +150,9 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST) { .unsupportedIfMemSizeNotPow2() .clampScalar(0, s8, s64) .widenScalarToNextPow2(0) + .lowerIf([=](const LegalityQuery &Query) { + return Query.Types[0].getSizeInBits() != Query.MMODescrs[0].Size * 8; + }) .clampNumElements(0, v2s32, v2s32); // Constants diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-extload.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-extload.mir new file mode 100644 index 0000000000000..c96788ac9b97a --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-extload.mir @@ -0,0 +1,25 @@ +# RUN: llc -O0 -run-pass=legalizer -global-isel %s -o - -verify-machineinstrs | FileCheck %s + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64--" + define void @test_extload(i8* %addr) { + entry: + ret void + } +... + +--- +name: test_extload +body: | + bb.0.entry: + liveins: $x0 + ; CHECK-LABEL: name: test_extload + ; CHECK: [[T0:%[0-9]+]]:_(p0) = COPY $x0 + ; CHECK: [[T1:%[0-9]+]]:_(s8) = G_LOAD [[T0]](p0) :: (load 1 from %ir.addr) + ; CHECK: [[T2:%[0-9]+]]:_(s32) = G_ANYEXT [[T1]](s8) + ; CHECK: $w0 = COPY [[T2]](s32) + %0:_(p0) = COPY $x0 + %1:_(s32) = G_LOAD %0 :: (load 1 from %ir.addr) + $w0 = COPY %1 +... diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-sextload.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-sextload.mir new file mode 100644 index 0000000000000..64dab81544520 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-sextload.mir @@ -0,0 +1,25 @@ +# RUN: llc -O0 -run-pass=legalizer -global-isel %s -o - -verify-machineinstrs | FileCheck %s + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64--" + define void @test_zextload(i8* %addr) { + entry: + ret void + } +... + +--- +name: test_zextload +body: | + bb.0.entry: + liveins: $x0 + ; CHECK-LABEL: name: test_zextload + ; CHECK: [[T0:%[0-9]+]]:_(p0) = COPY $x0 + ; CHECK: [[T1:%[0-9]+]]:_(s8) = G_LOAD [[T0]](p0) :: (load 1 from %ir.addr) + ; CHECK: [[T2:%[0-9]+]]:_(s32) = G_SEXT [[T1]](s8) + ; CHECK: $w0 = COPY [[T2]](s32) + %0:_(p0) = COPY $x0 + %1:_(s32) = G_SEXTLOAD %0 :: (load 1 from %ir.addr) + $w0 = COPY %1 +... diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-zextload.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-zextload.mir new file mode 100644 index 0000000000000..42bfb2dfb84cc --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-zextload.mir @@ -0,0 +1,25 @@ +# RUN: llc -O0 -run-pass=legalizer -global-isel %s -o - -verify-machineinstrs | FileCheck %s + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64--" + define void @test_sextload(i8* %addr) { + entry: + ret void + } +... + +--- +name: test_sextload +body: | + bb.0.entry: + liveins: $x0 + ; CHECK-LABEL: name: test_sextload + ; CHECK: [[T0:%[0-9]+]]:_(p0) = COPY $x0 + ; CHECK: [[T1:%[0-9]+]]:_(s8) = G_LOAD [[T0]](p0) :: (load 1 from %ir.addr) + ; CHECK: [[T2:%[0-9]+]]:_(s32) = G_ZEXT [[T1]](s8) + ; CHECK: $w0 = COPY [[T2]](s32) + %0:_(p0) = COPY $x0 + %1:_(s32) = G_ZEXTLOAD %0 :: (load 1 from %ir.addr) + $w0 = COPY %1 +...