Index: lib/Target/Mips/AsmParser/MipsAsmParser.cpp =================================================================== --- lib/Target/Mips/AsmParser/MipsAsmParser.cpp +++ lib/Target/Mips/AsmParser/MipsAsmParser.cpp @@ -82,6 +82,10 @@ MCSubtargetInfo &STI; MCAsmParser &Parser; MipsAssemblerOptions Options; + MCSymbol *CurrentFn; // Pointer to the function being parsed. It may be a + // nullptr, which indicates that no function is currently + // selected. This usually happens after an '.end func' + // directive. #define GET_ASSEMBLER_HEADER #include "MipsGenAsmMatcher.inc" @@ -285,6 +289,8 @@ if (!isABI_O32() && !useOddSPReg() != 0) report_fatal_error("-mno-odd-spreg requires the O32 ABI"); + + CurrentFn = nullptr; } MCAsmParser &getParser() const { return Parser; } @@ -3055,22 +3061,152 @@ parseDataDirective(8, DirectiveID.getLoc()); return false; } - if (IDVal == ".ent") { - // Ignore this directive for now. - Parser.Lex(); + StringRef SymbolName; + + if (Parser.parseIdentifier(SymbolName)) { + reportParseError("expected identifier after .ent"); + return false; + } + + // There's an undocumented extension that allows an integer to + // follow the name of the procedure which AFAICS is ignored by GAS. + // Example: .ent foo,2 + if (getLexer().isNot(AsmToken::EndOfStatement)) { + if (getLexer().isNot(AsmToken::Comma)) { + // Even though we accept this undocumented extension for compatibility + // reasons, the additional integer argument does not actually change + // the behaviour of the '.ent' directive. + // So, when we come across a comma with no second argument, + // we say that we expected the end of the statement and not a comma, + // instead of saying that we expected a second argument after it. + reportParseError("unexpected token, expected end of statement"); + return false; + } + Parser.Lex(); // Eat the comma. + const MCExpr *DummyNumber; + int64_t DummyNumberVal; + // If the user was explicitly trying to use the extended version, + // we still give helpful extension-related error messages. + if (Parser.parseExpression(DummyNumber)) { + reportParseError("expected number after comma"); + return false; + } + if (!DummyNumber->EvaluateAsAbsolute(DummyNumberVal)) { + reportParseError("expected an absolute expression after comma"); + return false; + } + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + MCSymbol *Sym = getContext().GetOrCreateSymbol(SymbolName); + + getTargetStreamer().emitDirectiveEnt(*Sym); + CurrentFn = Sym; return false; } if (IDVal == ".end") { - // Ignore this directive for now. - Parser.Lex(); + StringRef SymbolName; + + if (Parser.parseIdentifier(SymbolName)) { + reportParseError("expected identifier after .end"); + return false; + } + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + if (CurrentFn == nullptr) { + reportParseError(".end used without .ent"); + return false; + } + + if ((SymbolName != CurrentFn->getName())) { + reportParseError(".end symbol does not match .ent symbol"); + return false; + } + + getTargetStreamer().emitDirectiveEnd(SymbolName); + CurrentFn = nullptr; return false; } if (IDVal == ".frame") { - // Ignore this directive for now. - Parser.eatToEndOfStatement(); + // .frame $stack_reg, frame_size_in_bytes, $return_reg + SmallVector, 1> TmpReg; + OperandMatchResultTy ResTy = ParseAnyRegister(TmpReg); + if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { + reportParseError("expected stack register"); + return false; + } + + MipsOperand &StackRegOpnd = static_cast(*TmpReg[0]); + if (!StackRegOpnd.isGPRAsmReg()) { + reportParseError(StackRegOpnd.getStartLoc(), + "expected general purpose register"); + return false; + } + unsigned StackReg = StackRegOpnd.getGPR32Reg(); + + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token, expected comma"); + return false; + } + + // Parse the frame size. + const MCExpr *FrameSize; + int64_t FrameSizeVal; + + if (Parser.parseExpression(FrameSize)) { + reportParseError("expected frame size value"); + return false; + } + + if (!FrameSize->EvaluateAsAbsolute(FrameSizeVal)) { + reportParseError("frame size not an absolute expression"); + return false; + } + + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token, expected comma"); + return false; + } + + // Parse the return register. + TmpReg.clear(); + ResTy = ParseAnyRegister(TmpReg); + if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { + reportParseError("expected return register"); + return false; + } + + MipsOperand &ReturnRegOpnd = static_cast(*TmpReg[0]); + if (!ReturnRegOpnd.isGPRAsmReg()) { + reportParseError(ReturnRegOpnd.getStartLoc(), + "expected general purpose register"); + return false; + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + getTargetStreamer().emitFrame(StackReg, FrameSizeVal, + ReturnRegOpnd.getGPR32Reg()); return false; } @@ -3078,15 +3214,61 @@ return parseDirectiveSet(); } - if (IDVal == ".fmask") { - // Ignore this directive for now. - Parser.eatToEndOfStatement(); - return false; - } + if (IDVal == ".mask" || IDVal == ".fmask") { + // .mask bitmask, frame_offset + // bitmask: One bit for each register used. + // frame_offset: Offset from Canonical Frame Address ($sp on entry) where + // first register is expected to be saved. + // Examples: + // .mask 0x80000000, -4 + // .fmask 0x80000000, -4 + // - if (IDVal == ".mask") { - // Ignore this directive for now. - Parser.eatToEndOfStatement(); + // Parse the bitmask + const MCExpr *BitMask; + int64_t BitMaskVal; + + if (Parser.parseExpression(BitMask)) { + reportParseError("expected bitmask value"); + return false; + } + + if (!BitMask->EvaluateAsAbsolute(BitMaskVal)) { + reportParseError("bitmask not an absolute expression"); + return false; + } + + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token, expected comma"); + return false; + } + + // Parse the frame_offset + const MCExpr *FrameOffset; + int64_t FrameOffsetVal; + + if (Parser.parseExpression(FrameOffset)) { + reportParseError("expected frame offset value"); + return false; + } + + if (!FrameOffset->EvaluateAsAbsolute(FrameOffsetVal)) { + reportParseError("frame offset not an absolute expression"); + return false; + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + if (IDVal == ".mask") + getTargetStreamer().emitMask(BitMaskVal, FrameOffsetVal); + else + getTargetStreamer().emitFMask(BitMaskVal, FrameOffsetVal); return false; } Index: lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp =================================================================== --- lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp +++ lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp @@ -29,7 +29,9 @@ using namespace llvm; MipsTargetStreamer::MipsTargetStreamer(MCStreamer &S) - : MCTargetStreamer(S), canHaveModuleDirective(true) {} + : MCTargetStreamer(S), canHaveModuleDirective(true) { + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; +} void MipsTargetStreamer::emitDirectiveSetMicroMips() {} void MipsTargetStreamer::emitDirectiveSetNoMicroMips() {} void MipsTargetStreamer::emitDirectiveSetMips16() {} @@ -492,11 +494,45 @@ } void MipsTargetELFStreamer::emitDirectiveEnd(StringRef Name) { - // FIXME: implement. + MCAssembler &MCA = getStreamer().getAssembler(); + MCContext &Context = MCA.getContext(); + MCStreamer &OS = getStreamer(); + + const MCSectionELF *Sec = Context.getELFSection(".pdr", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHT_REL, + SectionKind::getMetadata()); + + const MCSymbolRefExpr *ExprRef = + MCSymbolRefExpr::Create(Name, MCSymbolRefExpr::VK_None, Context); + + MCSectionData &SecData = MCA.getOrCreateSectionData(*Sec); + SecData.setAlignment(4); + + OS.PushSection(); + + OS.SwitchSection(Sec); + + OS.EmitValueImpl(ExprRef, 4); + + OS.EmitIntValue(GPRInfoSet ? GPRBitMask : 0, 4); // reg_mask + OS.EmitIntValue(GPRInfoSet ? GPROffset : 0, 4); // reg_offset + + OS.EmitIntValue(FPRInfoSet ? FPRBitMask : 0, 4); // fpreg_mask + OS.EmitIntValue(FPRInfoSet ? FPROffset : 0, 4); // fpreg_offset + + OS.EmitIntValue(FrameInfoSet ? FrameOffset : 0, 4); // frame_offset + OS.EmitIntValue(FrameInfoSet ? FrameReg : 0, 4); // frame_reg + OS.EmitIntValue(FrameInfoSet ? ReturnReg : 0, 4); // return_reg + + // The .end directive marks the end of a procedure. Invalidate + // the information gathered up until this point. + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; + + OS.PopSection(); } void MipsTargetELFStreamer::emitDirectiveEnt(const MCSymbol &Symbol) { - // FIXME: implement. + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; } void MipsTargetELFStreamer::emitDirectiveAbiCalls() { @@ -542,18 +578,28 @@ } void MipsTargetELFStreamer::emitFrame(unsigned StackReg, unsigned StackSize, - unsigned ReturnReg) { - // FIXME: implement. + unsigned ReturnReg_) { + MCContext &Context = getStreamer().getAssembler().getContext(); + const MCRegisterInfo *RegInfo = Context.getRegisterInfo(); + + FrameInfoSet = true; + FrameReg = RegInfo->getEncodingValue(StackReg); + FrameOffset = StackSize; + ReturnReg = RegInfo->getEncodingValue(ReturnReg_); } void MipsTargetELFStreamer::emitMask(unsigned CPUBitmask, int CPUTopSavedRegOff) { - // FIXME: implement. + GPRInfoSet = true; + GPRBitMask = CPUBitmask; + GPROffset = CPUTopSavedRegOff; } void MipsTargetELFStreamer::emitFMask(unsigned FPUBitmask, int FPUTopSavedRegOff) { - // FIXME: implement. + FPRInfoSet = true; + FPRBitMask = FPUBitmask; + FPROffset = FPUTopSavedRegOff; } void MipsTargetELFStreamer::emitDirectiveSetMips1() { Index: lib/Target/Mips/MipsTargetStreamer.h =================================================================== --- lib/Target/Mips/MipsTargetStreamer.h +++ lib/Target/Mips/MipsTargetStreamer.h @@ -11,6 +11,7 @@ #define MIPSTARGETSTREAMER_H #include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "MCTargetDesc/MipsABIFlagsSection.h" @@ -97,6 +98,19 @@ protected: MipsABIFlagsSection ABIFlagsSection; + bool GPRInfoSet; + unsigned GPRBitMask; + int GPROffset; + + bool FPRInfoSet; + unsigned FPRBitMask; + int FPROffset; + + bool FrameInfoSet; + int FrameOffset; + unsigned FrameReg; + unsigned ReturnReg; + private: bool canHaveModuleDirective; }; Index: test/MC/Mips/mips-pdr-bad.s =================================================================== --- /dev/null +++ test/MC/Mips/mips-pdr-bad.s @@ -0,0 +1,42 @@ +# RUN: not llvm-mc %s -arch=mips -mcpu=mips32r2 2>%t1 +# RUN: FileCheck %s < %t1 -check-prefix=ASM + + .text + + .ent # ASM: :[[@LINE]]:14: error: expected identifier after .ent + .ent bar, # ASM: :[[@LINE]]:19: error: expected number after comma + .ent foo, bar # AMS: :[[@LINE]]:23: error: expected an absolute expression after comma + .ent foo, 5, bar # AMS: :[[@LINE]]:20: error: unexpected token, expected end of statement + + .frame # ASM: :[[@LINE]]:16: error: expected stack register + .frame bar # ASM: :[[@LINE]]:16: error: expected stack register + .frame $f1, 8, # ASM: :[[@LINE]]:16: error: expected general purpose register + .frame $sp # ASM: :[[@LINE]]:20: error: unexpected token, expected comma + .frame $sp, # ASM: :[[@LINE]]:21: error: expected frame size value + .frame $sp, bar # ASM: :[[@LINE]]:25: error: frame size not an absolute expression + .frame $sp, 8 # ASM: :[[@LINE]]:23: error: unexpected token, expected comma + .frame $sp, 8, # ASM: :[[@LINE]]:24: error: expected return register + .frame $sp, 8, $f1 # ASM: :[[@LINE]]:24: error: expected general purpose register + .frame $sp, 8, $ra, foo # ASM: :[[@LINE]]:27: error: unexpected token, expected end of statement + + .mask # ASM: :[[@LINE]]:16: error: expected bitmask value + .mask foo # ASM: :[[@LINE]]:19: error: bitmask not an absolute expression + .mask 0x80000000 # ASM: :[[@LINE]]:26: error: unexpected token, expected comma + .mask 0x80000000, # ASM: :[[@LINE]]:27: error: expected frame offset value + .mask 0x80000000, foo # ASM: :[[@LINE]]:31: error: frame offset not an absolute expression + .mask 0x80000000, -4, bar # ASM: :[[@LINE]]:29: error: unexpected token, expected end of statement + + .fmask # ASM: :[[@LINE]]:17: error: expected bitmask value + .fmask foo # ASM: :[[@LINE]]:20: error: bitmask not an absolute expression + .fmask 0x80000000 # ASM: :[[@LINE]]:27: error: unexpected token, expected comma + .fmask 0x80000000, # ASM: :[[@LINE]]:28: error: expected frame offset value + .fmask 0x80000000, foo # ASM: :[[@LINE]]:32: error: frame offset not an absolute expression + .fmask 0x80000000, -4, bar # ASM: :[[@LINE]]:30: error: unexpected token, expected end of statement + + .end # ASM: :[[@LINE]]:14: error: expected identifier after .end + .ent _local_foo_bar + .end _local_foo_bar, foo # ASM: :[[@LINE]]:28: error: unexpected token, expected end of statement + .end _local_foo_bar + .end _local_foo # ASM: :[[@LINE]]:25: error: .end used without .ent + .ent _local_foo, 2 + .end _local_foo_bar # ASM: :[[@LINE]]:29: error: .end symbol does not match .ent symbol Index: test/MC/Mips/mips-pdr.s =================================================================== --- /dev/null +++ test/MC/Mips/mips-pdr.s @@ -0,0 +1,64 @@ +# RUN: llvm-mc %s -arch=mips -mcpu=mips32r2 -filetype=asm | \ +# RUN: FileCheck %s -check-prefix=ASMOUT + +# RUN: llvm-mc %s -arch=mips -mcpu=mips32r2 -filetype=obj -o - | \ +# RUN: llvm-readobj -s -section-data | \ +# RUN: FileCheck %s -check-prefix=OBJOUT + +# ASMOUT: .text +# ASMOUT: .type _local_foo,@function +# ASMOUT: .ent _local_foo +# ASMOUT:_local_foo: +# ASMOUT: .frame $fp,16,$ra +# ASMOUT: .mask 0x10101010,-4 +# ASMOUT: .fmask 0x01010101,-8 +# ASMOUT: .end _local_foo +# ASMOUT: .size local_foo, + +# OBJOUT: Section { +# OBJOUT: Name: .pdr +# OBJOUT: Type: SHT_PROGBITS (0x1) +# OBJOUT: Flags [ (0xB) +# OBJOUT: SHF_ALLOC (0x2) +# OBJOUT: SHF_WRITE (0x1) +# OBJOUT: ] +# OBJOUT: Size: 64 +# OBJOUT: SectionData ( +# OBJOUT: 0000: 00000000 10101010 FFFFFFFC 01010101 +# OBJOUT: 0010: FFFFFFF8 00000010 0000001E 0000001F +# OBJOUT: 0020: 00000000 10101010 FFFFFFFC 01010101 +# OBJOUT: 0030: FFFFFFF8 00000010 0000001E 0000001F +# OBJOUT: ) +# OBJOUT: } + +# We should also check if relocation information was correctly generated. +# OBJOUT: Section { +# OBJOUT: Name: .rel.pdr +# OBJOUT: Type: SHT_REL (0x9) +# OBJOUT: Flags [ (0x0) +# OBJOUT: ] +# OBJOUT: Size: 16 +# OBJOUT: SectionData ( +# OBJOUT: 0000: 00000000 00000202 00000020 00000802 +# OBJOUT: ) +# OBJOUT: } + +.text + .type _local_foo,@function + .ent _local_foo +_local_foo: + .frame $fp,16,$ra + .mask 0x10101010,-4 + .fmask 0x01010101,-8 + .end _local_foo + .size local_foo,.-_local_foo + + .globl _global_foo + .type _global_foo,@function + .ent _global_foo +_global_foo: + .frame $fp,16,$ra + .mask 0x10101010,-4 + .fmask 0x01010101,-8 + .end _global_foo + .size global_foo,.-_global_foo