Index: lib/Target/Mips/AsmParser/MipsAsmParser.cpp =================================================================== --- lib/Target/Mips/AsmParser/MipsAsmParser.cpp +++ lib/Target/Mips/AsmParser/MipsAsmParser.cpp @@ -68,6 +68,10 @@ MCSubtargetInfo &STI; MCAsmParser &Parser; MipsAssemblerOptions Options; + MCSymbol *CurrentFn; // Pointer to the function being parsed. It may be NULL + // which indicates no function is currently selected. + // This usually happens after an '.end function' + // directive. #define GET_ASSEMBLER_HEADER #include "MipsGenAsmMatcher.inc" @@ -2613,22 +2617,125 @@ 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)) { + reportParseError("unexpected token in directive .ent"); + return false; + } + Parser.Lex(); // Eat the comma. + const MCExpr *DummyNumber; + if (Parser.parseExpression(DummyNumber)) + return false; + Parser.eatToEndOfStatement(); + } + + 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 in directive .end"); + return false; + } + + MCSymbol *Sym = getContext().LookupSymbol(SymbolName); + + if (CurrentFn == NULL) { + reportParseError(".end used without .ent"); + return false; + } + + if ((Sym == NULL) || (Sym->getName() != CurrentFn->getName())) { + reportParseError(".end symbol does not match .ent symbol"); + return false; + } + + getTargetStreamer().emitDirectiveEnd(*Sym); + CurrentFn = NULL; return false; } if (IDVal == ".frame") { - // Ignore this directive for now. + // .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(), "invalid register"); + return false; + } + unsigned StackReg = StackRegOpnd.getGPR32Reg(); + + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token in .frame directive"); + return false; + } + + // Parse the frame size. + const MCExpr *FrameSize; + int64_t FrameSizeVal; + + if (Parser.parseExpression(FrameSize)) + 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 in .frame directive"); + 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(), "invalid register"); + return false; + } + Parser.eatToEndOfStatement(); + getTargetStreamer().emitFrame(StackReg, FrameSizeVal, + ReturnRegOpnd.getGPR32Reg()); return false; } @@ -2636,15 +2743,41 @@ 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 + // + + // Parse the bitmask + int64_t BitMaskVal; + if (Parser.parseAbsoluteExpression(BitMaskVal)) { + reportParseError("bitmask not an absolute expression"); + return false; + } - if (IDVal == ".mask") { - // Ignore this directive for now. - Parser.eatToEndOfStatement(); + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token in directive"); + return false; + } + + // Parse the frame_offset + int64_t FrameOffsetVal; + if (Parser.parseAbsoluteExpression(FrameOffsetVal)) { + reportParseError("frame offset not an absolute expression"); + 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 @@ -30,7 +30,9 @@ // Pin vtable to this file. void MipsTargetStreamer::anchor() {} -MipsTargetStreamer::MipsTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} +MipsTargetStreamer::MipsTargetStreamer(MCStreamer &S) : MCTargetStreamer(S) { + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; +} MipsTargetAsmStreamer::MipsTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS) @@ -346,11 +348,60 @@ } void MipsTargetELFStreamer::emitDirectiveEnd(const MCSymbol &Symbol) { - // 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(&Symbol, Context); + + MCSectionData &SecData = MCA.getOrCreateSectionData(*Sec); + SecData.setAlignment(4); + + OS.PushSection(); + + OS.SwitchSection(Sec); + + OS.EmitValueImpl(ExprRef, 4); + + if (GPRInfoSet) { + OS.EmitIntValue(GPRBitMask, 4); + OS.EmitIntValue(GPROffset, 4); + } else { + OS.EmitIntValue(0, 4); // reg_mask + OS.EmitIntValue(0, 4); // reg_offset + } + + if (FPRInfoSet) { + OS.EmitIntValue(FPRBitMask, 4); + OS.EmitIntValue(FPROffset, 4); + } else { + OS.EmitIntValue(0, 4); // fpreg_mask + OS.EmitIntValue(0, 4); // fpreg_offset + } + + if (FrameInfoSet) { + OS.EmitIntValue(FrameOffset, 4); + OS.EmitIntValue(FrameReg, 4); + OS.EmitIntValue(ReturnReg, 4); + } else { + OS.EmitIntValue(0, 4); // frame_offset + OS.EmitIntValue(0, 4); // frame_reg + OS.EmitIntValue(0, 4); // return_reg + } + + // The .end directive marks the end of a procedure. Invalidate + // the information gathered up until this point. + GPRInfoSet = FrameInfoSet = FPRInfoSet = false; + + OS.PopSection(); } void MipsTargetELFStreamer::emitDirectiveEnt(const MCSymbol &Symbol) { - // FIXME: implement. + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; } void MipsTargetELFStreamer::emitDirectiveAbiCalls() { @@ -396,18 +447,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::emitDirectiveSetMips32R2() { 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" namespace llvm { @@ -52,6 +53,19 @@ virtual void emitDirectiveCpload(unsigned RegNo) = 0; virtual void emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, const MCSymbol &Sym, bool IsReg) = 0; +protected: + bool GPRInfoSet; + unsigned GPRBitMask; + int GPROffset; + + bool FPRInfoSet; + unsigned FPRBitMask; + int FPROffset; + + bool FrameInfoSet; + int FrameOffset; + unsigned FrameReg; + unsigned ReturnReg; }; // This part is for ascii assembly output Index: test/MC/Mips/mips-pdr-bad.s =================================================================== --- /dev/null +++ test/MC/Mips/mips-pdr-bad.s @@ -0,0 +1,18 @@ +# 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: unknown token in expression + .frame # ASM: :[[@LINE]]:16: error: expected stack register + .frame bar # ASM: :[[@LINE]]:16: error: expected stack register + .frame $sp, bar # ASM: :[[@LINE]]:25: error: frame size not an absolute expression + .frame $sp, 8, # ASM: :[[@LINE]]:24: error: expected return register + .mask # ASM: :[[@LINE]]:16: error: bitmask not an absolute expression + .mask 0x80000000 # ASM: :[[@LINE]]:26: error: unexpected token in directive + .mask 0x80000000, # ASM: :[[@LINE]]:27: error: frame offset not an absolute expression + .fmask 0x80000000 # ASM: :[[@LINE]]:27: error: unexpected token in directive + .fmask 0x80000000, # ASM: :[[@LINE]]:28: error: frame offset not an absolute expression + .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 00000702 +# 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