[XRay] Support for for tail calls for ARM no-Thumb
authorDean Michael Berris <dberris@google.com>
Tue, 18 Oct 2016 05:54:15 +0000 (05:54 +0000)
committerDean Michael Berris <dberris@google.com>
Tue, 18 Oct 2016 05:54:15 +0000 (05:54 +0000)
This patch adds simplified support for tail calls on ARM with XRay instrumentation.

Known issue: compiled with generic flags: `-O3 -g -fxray-instrument -Wall
-std=c++14  -ffunction-sections -fdata-sections` (this list doesn't include my
specific flags like --target=armv7-linux-gnueabihf etc.), the following program

    #include <cstdio>
    #include <cassert>
    #include <xray/xray_interface.h>

    [[clang::xray_always_instrument]] void __attribute__ ((noinline)) fC() {
      std::printf("In fC()\n");
    }

    [[clang::xray_always_instrument]] void __attribute__ ((noinline)) fB() {
      std::printf("In fB()\n");
      fC();
    }

    [[clang::xray_always_instrument]] void __attribute__ ((noinline)) fA() {
      std::printf("In fA()\n");
      fB();
    }

    // Avoid infinite recursion in case the logging function is instrumented (so calls logging
    //   function again).
    [[clang::xray_never_instrument]] void simplyPrint(int32_t functionId, XRayEntryType xret)
    {
      printf("XRay: functionId=%d type=%d.\n", int(functionId), int(xret));
    }

    int main(int argc, char* argv[]) {
      __xray_set_handler(simplyPrint);

      printf("Patching...\n");
      __xray_patch();
      fA();

      printf("Unpatching...\n");
      __xray_unpatch();
      fA();

      return 0;
    }

gives the following output:

    Patching...
    XRay: functionId=3 type=0.
    In fA()
    XRay: functionId=3 type=1.
    XRay: functionId=2 type=0.
    In fB()
    XRay: functionId=2 type=1.
    XRay: functionId=1 type=0.
    XRay: functionId=1 type=1.
    In fC()
    Unpatching...
    In fA()
    In fB()
    In fC()

So for function fC() the exit sled seems to be called too much before function
exit: before printing In fC().

Debugging shows that the above happens because printf from fC is also called as
a tail call. So first the exit sled of fC is executed, and only then printf is
jumped into. So it seems we can't do anything about this with the current
approach (i.e. within the simplification described in
https://reviews.llvm.org/D23988 ).

Differential Revision: https://reviews.llvm.org/D25030

llvm-svn: 284456

llvm/lib/CodeGen/XRayInstrumentation.cpp
llvm/lib/Target/ARM/ARMAsmPrinter.cpp
llvm/lib/Target/ARM/ARMAsmPrinter.h
llvm/lib/Target/ARM/ARMMCInstLower.cpp
llvm/test/CodeGen/ARM/xray-tail-call-sled.ll [new file with mode: 0644]

index 7a3ca9799bb93f1ee678c082db0b0cc8d03af5c3..e76bf6948cff81fefef0c1feefac34e4c7c80598 100644 (file)
@@ -95,10 +95,17 @@ void XRayInstrumentation::prependRetWithPatchableExit(MachineFunction &MF,
 {
   for (auto &MBB : MF) {
     for (auto &T : MBB.terminators()) {
+      unsigned Opc = 0;
       if (T.isReturn()) {
-        // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT
-        BuildMI(MBB, T, T.getDebugLoc(),
-                TII->get(TargetOpcode::PATCHABLE_FUNCTION_EXIT));
+        Opc = TargetOpcode::PATCHABLE_FUNCTION_EXIT;
+      }
+      if (TII->isTailCall(T)) {
+        Opc = TargetOpcode::PATCHABLE_TAIL_CALL;
+      }
+      if (Opc != 0) {
+        // Prepend the return instruction with PATCHABLE_FUNCTION_EXIT or
+        //   PATCHABLE_TAIL_CALL .
+        BuildMI(MBB, T, T.getDebugLoc(),TII->get(Opc));
       }
     }
   }
index bd95c33fc495a2c252fb9530ee45610d1b55b2bc..ad8a6ed0b6d190ab9fdb6af403040808e16c85b1 100644 (file)
@@ -2050,6 +2050,9 @@ void ARMAsmPrinter::EmitInstruction(const MachineInstr *MI) {
   case ARM::PATCHABLE_FUNCTION_EXIT:
     LowerPATCHABLE_FUNCTION_EXIT(*MI);
     return;
+  case ARM::PATCHABLE_TAIL_CALL:
+    LowerPATCHABLE_TAIL_CALL(*MI);
+    return;
   }
 
   MCInst TmpInst;
index 277f725fc7747bc865b8bb474a53cbce57bca4e4..79269df2ae369fd1485743cf517e09d3fb8725fe 100644 (file)
@@ -112,6 +112,7 @@ public:
   // XRay-specific lowering for ARM.
   void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI);
   void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI);
+  void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI);
   // Helper function that emits the XRay sleds we've collected for a particular
   // function.
   void EmitXRayTable();
index 19c91510c2595c7fdc5c792773b7758b7a50002e..79f61652d61c2528ea8d3a2093dec3ab010830f6 100644 (file)
@@ -219,6 +219,11 @@ void ARMAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI)
   EmitSled(MI, SledKind::FUNCTION_EXIT);
 }
 
+void ARMAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI)
+{
+  EmitSled(MI, SledKind::TAIL_CALL);
+}
+
 void ARMAsmPrinter::EmitXRayTable()
 {
   if (Sleds.empty())
diff --git a/llvm/test/CodeGen/ARM/xray-tail-call-sled.ll b/llvm/test/CodeGen/ARM/xray-tail-call-sled.ll
new file mode 100644 (file)
index 0000000..d62a3b7
--- /dev/null
@@ -0,0 +1,53 @@
+; RUN: llc -filetype=asm -o - -mtriple=armv7-unknown-linux-gnu < %s | FileCheck %s
+
+define i32 @callee() nounwind noinline uwtable "function-instrument"="xray-always" {
+; CHECK:       .p2align        2
+; CHECK-LABEL: Lxray_sled_0:
+; CHECK-NEXT:  b       #20
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-LABEL: Ltmp0:
+  ret i32 0
+; CHECK-NEXT:  mov     r0, #0
+; CHECK-NEXT:  .p2align        2
+; CHECK-LABEL: Lxray_sled_1:
+; CHECK-NEXT:  b       #20
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-LABEL: Ltmp1:
+; CHECK-NEXT:  bx      lr
+}
+
+define i32 @caller() nounwind noinline uwtable "function-instrument"="xray-always" {
+; CHECK:       .p2align        2
+; CHECK-LABEL: Lxray_sled_2:
+; CHECK-NEXT:  b       #20
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-LABEL: Ltmp2:
+; CHECK:       .p2align        2
+; CHECK-LABEL: Lxray_sled_3:
+; CHECK-NEXT:  b       #20
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-NEXT:  nop
+; CHECK-LABEL: Ltmp3:
+  %retval = tail call i32 @callee()
+; CHECK:       b       callee
+  ret i32 %retval
+}