GlobalISel: Handle llvm.localescape
authorMatt Arsenault <Matthew.Arsenault@amd.com>
Wed, 29 Jul 2020 13:48:26 +0000 (09:48 -0400)
committerMatt Arsenault <Matthew.Arsenault@amd.com>
Tue, 4 Aug 2020 19:19:02 +0000 (15:19 -0400)
This one is pretty easy and shrinks the list of unhandled
intrinsics. I'm not sure how relevant the insert point is. Using the
insert position of EntryBuilder will place this after
constants. SelectionDAG seems to end up emitting these after argument
copies and before anything else, but I don't think it really
matters. This also ends up emitting these in the opposite order from
SelectionDAG, but I don't think that matters either.

This also needs a fix to stop the later passes dropping this as a dead
instruction. DeadMachineInstructionElim's version of isDead special
cases LOCAL_ESCAPE for some reason, and I'm not sure why it's excluded
from MachineInstr::isLabel (or why isDead doesn't check it).

I also noticed DeadMachineInstructionElim never considers inline asm
as dead, but GlobalISel will drop asm with no constraints.

llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
llvm/lib/CodeGen/GlobalISel/Utils.cpp
llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-localescape.ll [new file with mode: 0644]
llvm/test/CodeGen/AArch64/GlobalISel/labels-are-not-dead.mir [new file with mode: 0644]

index be669ec..ac867b4 100644 (file)
@@ -29,6 +29,7 @@
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineMemOperand.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/StackProtector.h"
@@ -1658,6 +1659,33 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
       .addUse(getOrCreateVReg(*CI.getArgOperand(1)));
     return true;
   }
+  case Intrinsic::localescape: {
+    MachineBasicBlock &EntryMBB = MF->front();
+    StringRef EscapedName = GlobalValue::dropLLVMManglingEscape(MF->getName());
+
+    // Directly emit some LOCAL_ESCAPE machine instrs. Label assignment emission
+    // is the same on all targets.
+    for (unsigned Idx = 0, E = CI.getNumArgOperands(); Idx < E; ++Idx) {
+      Value *Arg = CI.getArgOperand(Idx)->stripPointerCasts();
+      if (isa<ConstantPointerNull>(Arg))
+        continue; // Skip null pointers. They represent a hole in index space.
+
+      int FI = getOrCreateFrameIndex(*cast<AllocaInst>(Arg));
+      MCSymbol *FrameAllocSym =
+          MF->getMMI().getContext().getOrCreateFrameAllocSymbol(EscapedName,
+                                                                Idx);
+
+      // This should be inserted at the start of the entry block.
+      auto LocalEscape =
+          MIRBuilder.buildInstrNoInsert(TargetOpcode::LOCAL_ESCAPE)
+              .addSym(FrameAllocSym)
+              .addFrameIndex(FI);
+
+      EntryMBB.insert(EntryMBB.begin(), LocalEscape);
+    }
+
+    return true;
+  }
 #define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC)  \
   case Intrinsic::INTRINSIC:
 #include "llvm/IR/ConstrainedOps.def"
index 4a7513f..7fc738a 100644 (file)
@@ -180,6 +180,14 @@ bool llvm::canReplaceReg(Register DstReg, Register SrcReg,
 
 bool llvm::isTriviallyDead(const MachineInstr &MI,
                            const MachineRegisterInfo &MRI) {
+  // FIXME: This logical is mostly duplicated with
+  // DeadMachineInstructionElim::isDead. Why is LOCAL_ESCAPE not considered in
+  // MachineInstr::isLabel?
+
+  // Don't delete frame allocation labels.
+  if (MI.getOpcode() == TargetOpcode::LOCAL_ESCAPE)
+    return false;
+
   // If we can move an instruction, we can remove it.  Otherwise, it has
   // a side-effect of some sort.
   bool SawStore = false;
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-localescape.ll b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-localescape.ll
new file mode 100644 (file)
index 0000000..60eaea4
--- /dev/null
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+; RUN: llc -global-isel -mtriple=arm64-windows -stop-after=irtranslator -o - %s | FileCheck %s
+
+define void @local_escape() {
+  ; CHECK-LABEL: name: local_escape
+  ; CHECK: bb.1 (%ir-block.0):
+  ; CHECK:   LOCAL_ESCAPE <mcsymbol .Llocal_escape$frame_escape_1>, %stack.1.b
+  ; CHECK:   LOCAL_ESCAPE <mcsymbol .Llocal_escape$frame_escape_0>, %stack.0.a
+  ; CHECK:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 42
+  ; CHECK:   [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 13
+  ; CHECK:   [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.a
+  ; CHECK:   [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.b
+  ; CHECK:   G_STORE [[C]](s32), [[FRAME_INDEX]](p0) :: (store 4 into %ir.a)
+  ; CHECK:   G_STORE [[C1]](s32), [[FRAME_INDEX1]](p0) :: (store 4 into %ir.b)
+  ; CHECK:   RET_ReallyLR
+  %a = alloca i32
+  %b = alloca i32, i32 2
+  call void (...) @llvm.localescape(i32* %a, i32* %b)
+  store i32 42, i32* %a
+  store i32 13, i32* %b
+  ret void
+}
+
+; Try some instructions before the localescape, and use a null
+define void @local_escape_insert_point() {
+  ; CHECK-LABEL: name: local_escape_insert_point
+  ; CHECK: bb.1 (%ir-block.0):
+  ; CHECK:   LOCAL_ESCAPE <mcsymbol .Llocal_escape_insert_point$frame_escape_2>, %stack.1.b
+  ; CHECK:   LOCAL_ESCAPE <mcsymbol .Llocal_escape_insert_point$frame_escape_0>, %stack.0.a
+  ; CHECK:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 42
+  ; CHECK:   [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 13
+  ; CHECK:   [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.a
+  ; CHECK:   [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1.b
+  ; CHECK:   G_STORE [[C]](s32), [[FRAME_INDEX]](p0) :: (store 4 into %ir.a)
+  ; CHECK:   G_STORE [[C1]](s32), [[FRAME_INDEX1]](p0) :: (store 4 into %ir.b)
+  ; CHECK:   RET_ReallyLR
+  %a = alloca i32
+  %b = alloca i32, i32 2
+  store i32 42, i32* %a
+  store i32 13, i32* %b
+  call void (...) @llvm.localescape(i32* %a, i32* null, i32* %b)
+  ret void
+}
+
+declare void @foo([128 x i32]*)
+
+; Check a cast of an alloca
+define void @local_escape_strip_ptr_cast() {
+  ; CHECK-LABEL: name: local_escape_strip_ptr_cast
+  ; CHECK: bb.1 (%ir-block.0):
+  ; CHECK:   LOCAL_ESCAPE <mcsymbol .Llocal_escape_strip_ptr_cast$frame_escape_0>, %stack.0.a
+  ; CHECK:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 42
+  ; CHECK:   [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0.a
+  ; CHECK:   G_STORE [[C]](s32), [[FRAME_INDEX]](p0) :: (store 4 into %ir.cast)
+  ; CHECK:   ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp
+  ; CHECK:   $x0 = COPY [[FRAME_INDEX]](p0)
+  ; CHECK:   BL @foo, csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit $x0
+  ; CHECK:   ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp
+  ; CHECK:   RET_ReallyLR
+  %a = alloca [128 x i32]
+  %cast = bitcast [128 x i32]* %a to i32*
+  store i32 42, i32* %cast
+  call void (...) @llvm.localescape(i32* %cast, i32* null)
+  call void @foo([128 x i32]* %a)
+  ret void
+}
+
+declare void @llvm.localescape(...) #0
+
+attributes #0 = { nounwind }
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/labels-are-not-dead.mir b/llvm/test/CodeGen/AArch64/GlobalISel/labels-are-not-dead.mir
new file mode 100644 (file)
index 0000000..ae7c7d3
--- /dev/null
@@ -0,0 +1,34 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -march=aarch64 -run-pass=legalizer %s -o - | FileCheck %s
+
+# The LOCAL_ESCAPE instructions should not be deleted as dead.
+
+---
+name: no_erase_local_escape
+tracksRegLiveness: true
+stack:
+  - { id: 0, size: 4, alignment: 4 }
+  - { id: 1, size: 8, alignment: 4 }
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: no_erase_local_escape
+    ; CHECK: LOCAL_ESCAPE <mcsymbol .Llocal_escape$frame_escape_0>, %stack.0
+    ; CHECK: LOCAL_ESCAPE <mcsymbol .Llocal_escape$frame_escape_1>, %stack.1
+    ; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 42
+    ; CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 13
+    ; CHECK: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0
+    ; CHECK: [[FRAME_INDEX1:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.1
+    ; CHECK: G_STORE [[C]](s32), [[FRAME_INDEX]](p0) :: (store 4)
+    ; CHECK: G_STORE [[C1]](s32), [[FRAME_INDEX1]](p0) :: (store 4)
+    ; CHECK: RET_ReallyLR
+    LOCAL_ESCAPE <mcsymbol .Llocal_escape$frame_escape_0>, %stack.0
+    LOCAL_ESCAPE <mcsymbol .Llocal_escape$frame_escape_1>, %stack.1
+    %2:_(s32) = G_CONSTANT i32 42
+    %3:_(s32) = G_CONSTANT i32 13
+    %0:_(p0) = G_FRAME_INDEX %stack.0
+    %1:_(p0) = G_FRAME_INDEX %stack.1
+    G_STORE %2(s32), %0(p0) :: (store 4)
+    G_STORE %3(s32), %1(p0) :: (store 4)
+    RET_ReallyLR
+
+...