[llvm] r199818 - Fix inline assembly that switches between ARM and Thumb modes

Greg Fitzgerald gregf at codeaurora.org
Wed Jan 22 10:32:36 PST 2014


Author: garious
Date: Wed Jan 22 12:32:35 2014
New Revision: 199818

URL: http://llvm.org/viewvc/llvm-project?rev=199818&view=rev
Log:
Fix inline assembly that switches between ARM and Thumb modes

This patch restores the ARM mode if the user's inline assembly
does not.  In the object streamer, it ensures that instructions
following the inline assembly are encoded correctly and that
correct mapping symbols are emitted.  For the asm streamer, it
emits a .arm or .thumb directive.

This patch does not ensure that the inline assembly contains
the ADR instruction to switch modes at runtime.

The problem we need to solve is code like this:

  int foo(int a, int b) {
    int r = a + b;
    asm volatile(
        ".align 2     \n"
        ".arm         \n"
        "add r0,r0,r0 \n"
    : : "r"(r));
    return r+1;
  }

If we compile this function in thumb mode then the inline assembly
will switch to arm mode. We need to make sure that we switch back to
thumb mode after emitting the inline assembly or we will incorrectly
encode the instructions that follow (i.e. the assembly instructions
for return r+1).

Based on patch by David Peixotto

Change-Id: Ib57f6d2d78a22afad5de8693fba6230ff56ba48b

Added:
    llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-arm.ll
    llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-thumb.ll
    llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode.ll
Modified:
    llvm/trunk/include/llvm/MC/MCStreamer.h
    llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
    llvm/trunk/lib/MC/MCStreamer.cpp
    llvm/trunk/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp

Modified: llvm/trunk/include/llvm/MC/MCStreamer.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/MC/MCStreamer.h?rev=199818&r1=199817&r2=199818&view=diff
==============================================================================
--- llvm/trunk/include/llvm/MC/MCStreamer.h (original)
+++ llvm/trunk/include/llvm/MC/MCStreamer.h Wed Jan 22 12:32:35 2014
@@ -33,6 +33,7 @@ class MCInstPrinter;
 class MCSection;
 class MCStreamer;
 class MCSymbol;
+class MCSubtargetInfo;
 class StringRef;
 class Twine;
 class raw_ostream;
@@ -74,6 +75,15 @@ public:
 
   // Allow a target to add behavior to the EmitLabel of MCStreamer.
   virtual void emitLabel(MCSymbol *Symbol);
+
+  /// Let the target do anything it needs to do after emitting inlineasm.
+  /// This callback can be used restore the original mode in case the
+  /// inlineasm contains directives to switch modes.
+  /// \p StartInfo - the original subtarget info before inline asm
+  /// \p EndInfo   - the final subtarget info after parsing the inline asm,
+  //                 or NULL if the value is unknown.
+  virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                MCSubtargetInfo *EndInfo) {}
 };
 
 // FIXME: declared here because it is used from
@@ -104,6 +114,8 @@ public:
   virtual void emitArch(unsigned Arch) = 0;
   virtual void finishAttributeSection() = 0;
   virtual void emitInst(uint32_t Inst, char Suffix = '\0') = 0;
+  virtual void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                MCSubtargetInfo *EndInfo);
 };
 
 /// MCStreamer - Streaming machine code generation interface.  This interface
@@ -690,6 +702,16 @@ public:
   /// indicated by the hasRawTextSupport() predicate.  By default this aborts.
   void EmitRawText(const Twine &String);
 
+  /// EmitInlineAsmEnd - Used to perform any cleanup needed after emitting
+  /// inline assembly. Provides the start and end subtarget info values.
+  /// The end subtarget info may be NULL if it is not know, for example, when
+  /// emitting the inline assembly as raw text.
+  virtual void EmitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                MCSubtargetInfo *EndInfo) {
+    if (TargetStreamer)
+      TargetStreamer->emitInlineAsmEnd(StartInfo, EndInfo);
+  }
+
   /// Flush - Causes any cached state to be written out.
   virtual void Flush() {}
 

Modified: llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp?rev=199818&r1=199817&r2=199818&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp (original)
+++ llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp Wed Jan 22 12:32:35 2014
@@ -34,6 +34,7 @@
 #include "llvm/Support/TargetRegistry.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetSubtargetInfo.h"
 using namespace llvm;
 
 namespace {
@@ -83,6 +84,7 @@ void AsmPrinter::EmitInlineAsm(StringRef
   // system assembler does.
   if (OutStreamer.hasRawTextSupport()) {
     OutStreamer.EmitRawText(Str);
+    OutStreamer.EmitInlineAsmEnd(TM.getSubtarget<MCSubtargetInfo>(), 0);
     return;
   }
 
@@ -115,14 +117,16 @@ void AsmPrinter::EmitInlineAsm(StringRef
                                                   OutContext, OutStreamer,
                                                   *MAI));
 
-  // FIXME: It would be nice if we can avoid createing a new instance of
-  // MCSubtargetInfo here given TargetSubtargetInfo is available. However,
-  // we have to watch out for asm directives which can change subtarget
-  // state. e.g. .code 16, .code 32.
-  OwningPtr<MCSubtargetInfo>
-    STI(TM.getTarget().createMCSubtargetInfo(TM.getTargetTriple(),
-                                             TM.getTargetCPU(),
-                                             TM.getTargetFeatureString()));
+  // Reuse the existing Subtarget, because the AsmParser may need to
+  // modify it.  For example, when switching between ARM and
+  // Thumb mode.
+  MCSubtargetInfo* STI =
+    const_cast<MCSubtargetInfo*>(&TM.getSubtarget<MCSubtargetInfo>());
+
+  // Preserve a copy of the original STI because the parser may modify it.
+  // The target can restore the original state in EmitInlineAsmEnd().
+  MCSubtargetInfo STIOrig = *STI;
+
   OwningPtr<MCTargetAsmParser>
     TAP(TM.getTarget().createMCAsmParser(*STI, *Parser, *MII));
   if (!TAP)
@@ -134,6 +138,7 @@ void AsmPrinter::EmitInlineAsm(StringRef
   // Don't implicitly switch to the text section before the asm.
   int Res = Parser->Run(/*NoInitialTextSection*/ true,
                         /*NoFinalize*/ true);
+  OutStreamer.EmitInlineAsmEnd(STIOrig, STI);
   if (Res && !HasDiagHandler)
     report_fatal_error("Error parsing inline asm\n");
 }

Modified: llvm/trunk/lib/MC/MCStreamer.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/MC/MCStreamer.cpp?rev=199818&r1=199817&r2=199818&view=diff
==============================================================================
--- llvm/trunk/lib/MC/MCStreamer.cpp (original)
+++ llvm/trunk/lib/MC/MCStreamer.cpp Wed Jan 22 12:32:35 2014
@@ -25,7 +25,6 @@ using namespace llvm;
 // Pin the vtables to this file.
 MCTargetStreamer::~MCTargetStreamer() {}
 void MCTargetStreamer::emitLabel(MCSymbol *Symbol) {}
-void ARMTargetStreamer::anchor() {}
 
 MCStreamer::MCStreamer(MCContext &Ctx, MCTargetStreamer *TargetStreamer)
     : Context(Ctx), TargetStreamer(TargetStreamer), EmitEHFrame(true),

Modified: llvm/trunk/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp?rev=199818&r1=199817&r2=199818&view=diff
==============================================================================
--- llvm/trunk/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp (original)
+++ llvm/trunk/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp Wed Jan 22 12:32:35 2014
@@ -104,6 +104,25 @@ static unsigned GetArchDefaultCPUArch(un
   return 0;
 }
 
+static bool isThumb(const MCSubtargetInfo& STI) {
+  return (STI.getFeatureBits() & ARM::ModeThumb) != 0;
+}
+
+void ARMTargetStreamer::anchor() {}
+
+void ARMTargetStreamer::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
+                                         MCSubtargetInfo *EndInfo) {
+  // If either end mode is unknown (EndInfo == NULL) or different than
+  // the start mode, then restore the start mode.
+  const bool WasThumb = isThumb(StartInfo);
+  if (EndInfo == NULL || WasThumb != isThumb(*EndInfo)) {
+    assert(Streamer);
+    Streamer->EmitAssemblerFlag(WasThumb ? MCAF_Code16 : MCAF_Code32);
+    if (EndInfo)
+      EndInfo->ToggleFeature(ARM::ModeThumb);
+  }
+}
+
 namespace {
 
 class ARMELFStreamer;

Added: llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-arm.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-arm.ll?rev=199818&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-arm.ll (added)
+++ llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-arm.ll Wed Jan 22 12:32:35 2014
@@ -0,0 +1,18 @@
+;RUN:  llc -mtriple=armv7-linux-gnueabi -filetype=obj < %s | llvm-objdump -triple=armv7 -d - | FileCheck %s
+;RUN:  llc -mtriple=armv7-linux-gnueabi < %s | FileCheck %s -check-prefix=ASM
+;RUN:  llc -mtriple=armv7-apple-darwin < %s | FileCheck %s -check-prefix=ASM
+
+define hidden i32 @bah(i8* %start) #0 align 2 {
+  %1 = ptrtoint i8* %start to i32
+  %2 = tail call i32 asm sideeffect "@ Enter THUMB Mode\0A\09adr r3, 2f+1 \0A\09bx  r3 \0A\09.code 16 \0A2: push {r7} \0A\09mov r7, $4 \0A\09svc 0x0 \0A\09pop {r7} \0A\09", "={r0},{r0},{r1},{r2},r,~{r3}"(i32 %1, i32 %1, i32 0, i32 983042) #3
+  %3 = add i32 %1, 1
+  ret i32 %3
+}
+; CHECK: $t
+; CHECK: $a
+; CHECK: 01 00 81 e2     add     r0, r1, #1
+
+; .code 32 is implicit
+; ASM-LABEL: bah:
+; ASM: .code 16
+; ASM: .code 32

Added: llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-thumb.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-thumb.ll?rev=199818&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-thumb.ll (added)
+++ llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-thumb.ll Wed Jan 22 12:32:35 2014
@@ -0,0 +1,18 @@
+;RUN:  llc -mtriple=thumbv7-linux-gnueabi -filetype=obj < %s | llvm-objdump -triple=thumbv7 -d - | FileCheck %s
+;RUN:  llc -mtriple=thumbv7-linux-gnueabi < %s | FileCheck %s -check-prefix=ASM
+;RUN:  llc -mtriple=thumbv7-apple-darwin < %s | FileCheck %s -check-prefix=ASM
+
+define hidden i32 @bah(i8* %start) #0 align 2 {
+  %1 = ptrtoint i8* %start to i32
+  %2 = tail call i32 asm sideeffect "@ Enter ARM Mode  \0A\09adr r3, 1f \0A\09bx  r3 \0A\09.align 2 \0A\09.code 32 \0A1:  push {r7} \0A\09mov r7, $4 \0A\09svc 0x0 \0A\09pop {r7} \0A\09", "={r0},{r0},{r1},{r2},r,~{r3}"(i32 %1, i32 %1, i32 0, i32 983042) #3
+  %3 = add i32 %1, 1
+  ret i32 %3
+}
+; CHECK: $a
+; CHECK: $t
+; CHECK: 48 1c   adds    r0, r1, #1
+
+; ASM: .code 16
+; ASM-LABEL: bah:
+; ASM: .code 32
+; ASM: .code 16

Added: llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode.ll?rev=199818&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode.ll (added)
+++ llvm/trunk/test/CodeGen/ARM/inlineasm-switch-mode.ll Wed Jan 22 12:32:35 2014
@@ -0,0 +1,22 @@
+;RUN: llc -mtriple=thumbv7-linux-gnueabi -filetype=obj < %s > %t
+; Two pass decoding needed because llvm-objdump does not respect mapping symbols
+;RUN: llvm-objdump -triple=armv7   -d %t | FileCheck %s --check-prefix=ARM
+;RUN: llvm-objdump -triple=thumbv7 -d %t | FileCheck %s --check-prefix=THUMB
+
+define hidden i32 @bah(i8* %start) #0 align 2 {
+  %1 = ptrtoint i8* %start to i32
+  %2 = tail call i32 asm sideeffect "@ Enter ARM Mode  \0A\09adr r3, 1f \0A\09bx  r3 \0A\09.align 2 \0A\09.code 32 \0A1:  push {r7} \0A\09mov r7, $4 \0A\09svc 0x0 \0A\09pop {r7} \0A\09@ Enter THUMB Mode\0A\09adr r3, 2f+1 \0A\09bx  r3 \0A\09.code 16 \0A2: \0A\09", "={r0},{r0},{r1},{r2},r,~{r3}"(i32 %1, i32 %1, i32 0, i32 983042) #3
+  %3 = add i32 %1, 1
+  ret i32 %3
+}
+
+; ARM: $a
+; ARM-NEXT: 04 70 2d e5     str     r7, [sp, #-4]!
+; ARM: $t
+; ARM-NEXT: 48 1c
+
+; THUMB: $a
+; THUMB-NEXT: 04 70
+; THUMB-NEXT: 2d e5
+; THUMB: $t
+; THUMB-NEXT: 48 1c   adds    r0, r1, #1





More information about the llvm-commits mailing list