Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -33,6 +33,7 @@ class MCSection; class MCStreamer; class MCSymbol; +class MCSubtargetInfo; class StringRef; class Twine; class raw_ostream; @@ -74,6 +75,15 @@ // 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 @@ -101,6 +111,8 @@ 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 @@ -687,6 +699,16 @@ /// 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() {} Index: lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp @@ -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 @@ // system assembler does. if (OutStreamer.hasRawTextSupport()) { OutStreamer.EmitRawText(Str); + OutStreamer.EmitInlineAsmEnd(TM.getSubtarget(), 0); return; } @@ -115,14 +117,16 @@ 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 - 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(&TM.getSubtarget()); + + // 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 TAP(TM.getTarget().createMCAsmParser(*STI, *Parser, *MII)); if (!TAP) @@ -134,6 +138,7 @@ // 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"); } Index: lib/MC/MCStreamer.cpp =================================================================== --- lib/MC/MCStreamer.cpp +++ lib/MC/MCStreamer.cpp @@ -25,7 +25,6 @@ // 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), Index: lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp =================================================================== --- lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -104,6 +104,25 @@ 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; Index: test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-arm.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-arm.ll @@ -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 Index: test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-thumb.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/inlineasm-switch-mode-oneway-from-thumb.ll @@ -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 Index: test/CodeGen/ARM/inlineasm-switch-mode.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/inlineasm-switch-mode.ll @@ -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