summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/MC/MCStreamer.h2
-rw-r--r--lib/Target/ARM/AsmParser/ARMAsmParser.cpp84
-rw-r--r--lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp26
-rw-r--r--lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h6
-rw-r--r--test/MC/ARM/eh-directive-unwind_raw-diagnostics.s73
-rw-r--r--test/MC/ARM/eh-directive-unwind_raw.s113
6 files changed, 304 insertions, 0 deletions
diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h
index 946baf1b7c4..eb0eb7f19f2 100644
--- a/include/llvm/MC/MCStreamer.h
+++ b/include/llvm/MC/MCStreamer.h
@@ -92,6 +92,8 @@ public:
virtual void emitPad(int64_t Offset) = 0;
virtual void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
bool isVector) = 0;
+ virtual void emitUnwindRaw(int64_t StackOffset,
+ const SmallVectorImpl<uint8_t> &Opcodes) = 0;
virtual void switchVendor(StringRef Vendor) = 0;
virtual void emitAttribute(unsigned Attribute, unsigned Value) = 0;
diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index 0a1ba9105a7..dc9a308bb95 100644
--- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -295,6 +295,7 @@ class ARMAsmParser : public MCTargetAsmParser {
bool parseDirectiveLtorg(SMLoc L);
bool parseDirectiveEven(SMLoc L);
bool parseDirectivePersonalityIndex(SMLoc L);
+ bool parseDirectiveUnwindRaw(SMLoc L);
StringRef splitMnemonic(StringRef Mnemonic, unsigned &PredicationCode,
bool &CarrySetting, unsigned &ProcessorIMod,
@@ -8081,6 +8082,8 @@ bool ARMAsmParser::ParseDirective(AsmToken DirectiveID) {
return parseDirectiveEven(DirectiveID.getLoc());
else if (IDVal == ".personalityindex")
return parseDirectivePersonalityIndex(DirectiveID.getLoc());
+ else if (IDVal == ".unwind_raw")
+ return parseDirectiveUnwindRaw(DirectiveID.getLoc());
return true;
}
@@ -8915,6 +8918,87 @@ bool ARMAsmParser::parseDirectivePersonalityIndex(SMLoc L) {
return false;
}
+/// parseDirectiveUnwindRaw
+/// ::= .unwind_raw offset, opcode [, opcode...]
+bool ARMAsmParser::parseDirectiveUnwindRaw(SMLoc L) {
+ if (!UC.hasFnStart()) {
+ Parser.eatToEndOfStatement();
+ Error(L, ".fnstart must precede .unwind_raw directives");
+ return false;
+ }
+
+ int64_t StackOffset;
+
+ const MCExpr *OffsetExpr;
+ SMLoc OffsetLoc = getLexer().getLoc();
+ if (getLexer().is(AsmToken::EndOfStatement) ||
+ getParser().parseExpression(OffsetExpr)) {
+ Error(OffsetLoc, "expected expression");
+ Parser.eatToEndOfStatement();
+ return false;
+ }
+
+ const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(OffsetExpr);
+ if (!CE) {
+ Error(OffsetLoc, "offset must be a constant");
+ Parser.eatToEndOfStatement();
+ return false;
+ }
+
+ StackOffset = CE->getValue();
+
+ if (getLexer().isNot(AsmToken::Comma)) {
+ Error(getLexer().getLoc(), "expected comma");
+ Parser.eatToEndOfStatement();
+ return false;
+ }
+ Parser.Lex();
+
+ SmallVector<uint8_t, 16> Opcodes;
+ for (;;) {
+ const MCExpr *OE;
+
+ SMLoc OpcodeLoc = getLexer().getLoc();
+ if (getLexer().is(AsmToken::EndOfStatement) || Parser.parseExpression(OE)) {
+ Error(OpcodeLoc, "expected opcode expression");
+ Parser.eatToEndOfStatement();
+ return false;
+ }
+
+ const MCConstantExpr *OC = dyn_cast<MCConstantExpr>(OE);
+ if (!OC) {
+ Error(OpcodeLoc, "opcode value must be a constant");
+ Parser.eatToEndOfStatement();
+ return false;
+ }
+
+ const int64_t Opcode = OC->getValue();
+ if (Opcode & ~0xff) {
+ Error(OpcodeLoc, "invalid opcode");
+ Parser.eatToEndOfStatement();
+ return false;
+ }
+
+ Opcodes.push_back(uint8_t(Opcode));
+
+ if (getLexer().is(AsmToken::EndOfStatement))
+ break;
+
+ if (getLexer().isNot(AsmToken::Comma)) {
+ Error(getLexer().getLoc(), "unexpected token in directive");
+ Parser.eatToEndOfStatement();
+ return false;
+ }
+
+ Parser.Lex();
+ }
+
+ getTargetStreamer().emitUnwindRaw(StackOffset, Opcodes);
+
+ Parser.Lex();
+ return false;
+}
+
/// Force static initialization.
extern "C" void LLVMInitializeARMAsmParser() {
RegisterMCAsmParser<ARMAsmParser> X(TheARMTarget);
diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
index 82543a45f24..79d5619b764 100644
--- a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
+++ b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
@@ -123,6 +123,8 @@ class ARMTargetAsmStreamer : public ARMTargetStreamer {
virtual void emitPad(int64_t Offset);
virtual void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
bool isVector);
+ virtual void emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes);
virtual void switchVendor(StringRef Vendor);
virtual void emitAttribute(unsigned Attribute, unsigned Value);
@@ -242,6 +244,16 @@ void ARMTargetAsmStreamer::emitInst(uint32_t Inst, char Suffix) {
OS << "\t0x" << utohexstr(Inst) << "\n";
}
+void ARMTargetAsmStreamer::emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes) {
+ OS << "\t.unwind_raw " << Offset;
+ for (SmallVectorImpl<uint8_t>::const_iterator OCI = Opcodes.begin(),
+ OCE = Opcodes.end();
+ OCI != OCE; ++OCI)
+ OS << ", 0x" << utohexstr(*OCI);
+ OS << '\n';
+}
+
class ARMTargetELFStreamer : public ARMTargetStreamer {
private:
// This structure holds all attributes, accounting for
@@ -367,6 +379,8 @@ private:
virtual void emitPad(int64_t Offset);
virtual void emitRegSave(const SmallVectorImpl<unsigned> &RegList,
bool isVector);
+ virtual void emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes);
virtual void switchVendor(StringRef Vendor);
virtual void emitAttribute(unsigned Attribute, unsigned Value);
@@ -425,6 +439,7 @@ public:
void emitSetFP(unsigned NewFpReg, unsigned NewSpReg, int64_t Offset = 0);
void emitPad(int64_t Offset);
void emitRegSave(const SmallVectorImpl<unsigned> &RegList, bool isVector);
+ void emitUnwindRaw(int64_t Offset, const SmallVectorImpl<uint8_t> &Opcodes);
virtual void ChangeSection(const MCSection *Section,
const MCExpr *Subsection) {
@@ -637,6 +652,10 @@ void ARMTargetELFStreamer::emitRegSave(const SmallVectorImpl<unsigned> &RegList,
bool isVector) {
getStreamer().emitRegSave(RegList, isVector);
}
+void ARMTargetELFStreamer::emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes) {
+ getStreamer().emitUnwindRaw(Offset, Opcodes);
+}
void ARMTargetELFStreamer::switchVendor(StringRef Vendor) {
assert(!Vendor.empty() && "Vendor cannot be empty.");
@@ -1202,6 +1221,13 @@ void ARMELFStreamer::emitRegSave(const SmallVectorImpl<unsigned> &RegList,
UnwindOpAsm.EmitRegSave(Mask);
}
+void ARMELFStreamer::emitUnwindRaw(int64_t Offset,
+ const SmallVectorImpl<uint8_t> &Opcodes) {
+ FlushPendingOffset();
+ SPOffset = SPOffset - Offset;
+ UnwindOpAsm.EmitRaw(Opcodes);
+}
+
namespace llvm {
MCStreamer *createMCAsmStreamer(MCContext &Ctx, formatted_raw_ostream &OS,
diff --git a/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h b/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h
index b7937535761..e2c44008144 100644
--- a/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h
+++ b/lib/Target/ARM/MCTargetDesc/ARMUnwindOpAsm.h
@@ -61,6 +61,12 @@ public:
/// Emit unwind opcodes to add $sp with an offset.
void EmitSPOffset(int64_t Offset);
+ /// Emit unwind raw opcodes
+ void EmitRaw(const SmallVectorImpl<uint8_t> &Opcodes) {
+ Ops.insert(Ops.end(), Opcodes.begin(), Opcodes.end());
+ OpBegins.push_back(OpBegins.back() + Opcodes.size());
+ }
+
/// Finalize the unwind opcode sequence for EmitBytes()
void Finalize(unsigned &PersonalityIndex,
SmallVectorImpl<uint8_t> &Result);
diff --git a/test/MC/ARM/eh-directive-unwind_raw-diagnostics.s b/test/MC/ARM/eh-directive-unwind_raw-diagnostics.s
new file mode 100644
index 00000000000..72a208ed83d
--- /dev/null
+++ b/test/MC/ARM/eh-directive-unwind_raw-diagnostics.s
@@ -0,0 +1,73 @@
+@ RUN: not llvm-mc -triple armv7-linux-eabi -filetype asm -o /dev/null 2>&1 %s \
+@ RUN: | FileCheck %s
+
+ .syntax unified
+
+ .type require_fnstart,%function
+require_fnstart:
+ .unwind_raw 0, 0
+
+@ CHECK: error: .fnstart must precede .unwind_raw directive
+@ CHECK: .unwind_raw 0, 0
+@ CHECK: ^
+
+ .type check_arguments,%function
+check_arguments:
+ .fnstart
+ .unwind_raw
+ .fnend
+
+@ CHECK: error: expected expression
+@ CHECK: .unwind_raw
+@ CHECK: ^
+
+ .type check_stack_offset,%function
+check_stack_offset:
+ .fnstart
+ .unwind_raw ., 0
+ .fnend
+
+@ CHECK: error: offset must be a constant
+@ CHECK: .unwind_raw ., 0
+@ CHECK: ^
+
+ .type comma_check,%function
+comma_check:
+ .fnstart
+ .unwind_raw 0
+ .fnend
+
+@ CHECK: error: expected comma
+@ CHECK: .unwind_raw 0
+@ CHECK: ^
+
+ .type require_opcode,%function
+require_opcode:
+ .fnstart
+ .unwind_raw 0,
+ .fnend
+
+@ CHECK: error: expected opcode expression
+@ CHECK: .unwind_raw 0,
+@ CHECK: ^
+
+ .type require_opcode_constant,%function
+require_opcode_constant:
+ .fnstart
+ .unwind_raw 0, .
+ .fnend
+
+@ CHECK: error: opcode value must be a constant
+@ CHECK: .unwind_raw 0, .
+@ CHECK: ^
+
+ .type check_opcode_range,%function
+check_opcode_range:
+ .fnstart
+ .unwind_raw 0, 0x100
+ .fnend
+
+@ CHECK: error: invalid opcode
+@ CHECK: .unwind_raw 0, 0x100
+@ CHECK: ^
+
diff --git a/test/MC/ARM/eh-directive-unwind_raw.s b/test/MC/ARM/eh-directive-unwind_raw.s
new file mode 100644
index 00000000000..ab826d58e63
--- /dev/null
+++ b/test/MC/ARM/eh-directive-unwind_raw.s
@@ -0,0 +1,113 @@
+@ RUN: llvm-mc -triple armv7-linux-eabi -filetype obj -o - %s | llvm-readobj -u \
+@ RUN: | FileCheck %s
+
+ .syntax unified
+
+ .type save,%function
+ .thumb_func
+save:
+ .fnstart
+ .unwind_raw 4, 0xb1, 0x01
+ push {r0}
+ pop {r0}
+ bx lr
+ .fnend
+
+ .type empty,%function
+ .thumb_func
+empty:
+ .fnstart
+ .unwind_raw 0, 0xb0
+ bx lr
+ .fnend
+
+ .type extended,%function
+ .thumb_func
+extended:
+ .fnstart
+ .unwind_raw 12, 0x9b, 0x40, 0x84, 0x80, 0xb0, 0xb0
+ @ .save {fp, lr}
+ stmfd sp!, {fp, lr}
+ @ .setfp fp, sp, #4
+ add fp, sp, #4
+ @ .pad #8
+ sub sp, sp, #8
+ add sp, sp, #8
+ sub fp, sp, #4
+ ldmfd sp!, {fp, lr}
+ bx lr
+ .fnend
+
+ .type refuse,%function
+ .thumb_func
+refuse:
+ .fnstart
+ .unwind_raw 0, 0x80, 0x00
+ bx lr
+ .fnend
+
+ .type stack_adjust,%function
+ .thumb_func
+stack_adjust:
+ .fnstart
+ .setfp fp, sp, #32
+ .unwind_raw 24, 0xc2
+ .fnend
+
+@ CHECK: UnwindInformation {
+@ CHECK: UnwindIndexTable {
+@ CHECK: SectionName: .ARM.exidx
+@ CHECK: Entries [
+@ CHECK: Entry {
+@ CHECK: Model: Compact (Inline)
+@ CHECK: PersonalityIndex: 0
+@ CHECK: Opcodes [
+@ CHECK: Opcode: 0xB1
+@ CHECK: Opcode: 0x1
+@ CHECK: Opcode: 0xB0
+@ CHECK: ]
+@ CHECK: }
+@ CHECK: Entry {
+@ CHECK: Model: Compact (Inline)
+@ CHECK: PersonalityIndex: 0
+@ CHECK: Opcodes [
+@ CHECK: Opcode: 0xB0
+@ CHECK: Opcode: 0xB0
+@ CHECK: Opcode: 0xB0
+@ CHECK: ]
+@ CHECK: }
+@ CHECK: Entry {
+@ CHECK: ExceptionHandlingTable: .ARM.extab
+@ CHECK: Model: Compact
+@ CHECK: PersonalityIndex: 1
+@ CHECK: Opcodes [
+@ CHECK: Opcode: 0x9B
+@ CHECK: Opcode: 0x40
+@ CHECK: Opcode: 0x84
+@ CHECK: Opcode: 0x80
+@ CHECK: Opcode: 0xB0
+@ CHECK: Opcode: 0xB0
+@ CHECK: ]
+@ CHECK: }
+@ CHECK: Entry {
+@ CHECK: Model: Compact (Inline)
+@ CHECK: PersonalityIndex: 0
+@ CHECK: Opcodes [
+@ CHECK: Opcode: 0x80
+@ CHECK: Opcode: 0x0
+@ CHECK: Opcode: 0xB0
+@ CHECK: ]
+@ CHECK: }
+@ CHECK: Entry {
+@ CHECK: Model: Compact (Inline)
+@ CHECK: PersonalityIndex: 0
+@ CHECK: Opcodes [
+@ CHECK: Opcode: 0x9B
+@ CHECK: Opcode: 0x4D
+@ CHECK: Opcode: 0xC2
+@ CHECK: ]
+@ CHECK: }
+@ CHECK: ]
+@ CHECK: }
+@ CHECK: }
+