Index: lib/CodeGen/XRayInstrumentation.cpp =================================================================== --- lib/CodeGen/XRayInstrumentation.cpp +++ lib/CodeGen/XRayInstrumentation.cpp @@ -158,6 +158,10 @@ case Triple::ArchType::thumb: case Triple::ArchType::aarch64: case Triple::ArchType::ppc64le: + case Triple::ArchType::mips: + case Triple::ArchType::mipsel: + case Triple::ArchType::mips64: + case Triple::ArchType::mips64el: // For the architectures which don't have a single return instruction prependRetWithPatchableExit(MF, TII); break; Index: lib/Target/Mips/MipsAsmPrinter.h =================================================================== --- lib/Target/Mips/MipsAsmPrinter.h +++ lib/Target/Mips/MipsAsmPrinter.h @@ -35,7 +35,21 @@ void EmitInstrWithMacroNoAT(const MachineInstr *MI); + //===------------------------------------------------------------------===// + // XRay implementation + //===------------------------------------------------------------------===// +public: + // XRay-specific lowering for Mips. + void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI); + void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI); + void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI); + // Helper function that emits the XRay sleds we've collected for a particular + // function. + void EmitXRayTable(); + private: + void EmitSled(const MachineInstr &MI, SledKind Kind); + // tblgen'erated function. bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, const MachineInstr *MI); Index: lib/Target/Mips/MipsAsmPrinter.cpp =================================================================== --- lib/Target/Mips/MipsAsmPrinter.cpp +++ lib/Target/Mips/MipsAsmPrinter.cpp @@ -39,6 +39,7 @@ #include "llvm/MC/MCELFStreamer.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSymbolELF.h" @@ -79,6 +80,9 @@ NaClAlignIndirectJumpTargets(MF); AsmPrinter::runOnMachineFunction(MF); + + EmitXRayTable(); + return true; } @@ -132,6 +136,7 @@ void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) { MipsTargetStreamer &TS = getTargetStreamer(); + unsigned Opc = MI->getOpcode(); TS.forbidModuleDirective(); if (MI->isDebugValue()) { @@ -143,20 +148,20 @@ } // If we just ended a constant pool, mark it as such. - if (InConstantPool && MI->getOpcode() != Mips::CONSTPOOL_ENTRY) { + if (InConstantPool && Opc != Mips::CONSTPOOL_ENTRY) { OutStreamer->EmitDataRegion(MCDR_DataRegionEnd); InConstantPool = false; } - if (MI->getOpcode() == Mips::CONSTPOOL_ENTRY) { + if (Opc == Mips::CONSTPOOL_ENTRY) { // CONSTPOOL_ENTRY - This instruction represents a floating - //constant pool in the function. The first operand is the ID# + // constant pool in the function. The first operand is the ID# // for this instruction, the second is the index into the // MachineConstantPool that this is, the third is the size in // bytes of this constant pool entry. // The required alignment is specified on the basic block holding this MI. // unsigned LabelId = (unsigned)MI->getOperand(0).getImm(); - unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex(); + unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex(); // If this is the first entry of the pool, mark it. if (!InConstantPool) { @@ -174,6 +179,17 @@ return; } + switch (Opc) { + case Mips::PATCHABLE_FUNCTION_ENTER: + LowerPATCHABLE_FUNCTION_ENTER(*MI); + return; + case Mips::PATCHABLE_FUNCTION_EXIT: + LowerPATCHABLE_FUNCTION_EXIT(*MI); + return; + case Mips::PATCHABLE_TAIL_CALL: + LowerPATCHABLE_TAIL_CALL(*MI); + return; + } MachineBasicBlock::const_instr_iterator I = MI->getIterator(); MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end(); @@ -1034,6 +1050,149 @@ OutStreamer->SwitchSection(OutContext.getObjectFileInfo()->getTextSection()); } +void MipsAsmPrinter::EmitSled(const MachineInstr &MI, SledKind Kind) { + const uint8_t NoopsInSledCount = Subtarget->isGP64bit() ? 15 : 11; + // For mips32 we want to emit the following pattern: + // + // .Lxray_sled_N: + // ALIGN + // B .tmpN + // 11 NOP instructions (44 bytes) + // ADDIU T9, T9, 52 + // .tmpN + // + // We need the 44 bytes (11 instructions) because at runtime, we'd + // be patching over the full 48 bytes (12 instructions) with the following + // pattern: + // + // ADDIU SP, SP, -8 + // NOP + // SW RA, 4(SP) + // SW T9, 0(SP) + // LUI T9, %hi(__xray_FunctionEntry/Exit) + // ORI T9, T9, %lo(__xray_FunctionEntry/Exit) + // LUI T0, %hi(function_id) + // JALR T9 + // ORI T0, T0, %lo(function_id) + // LW T9, 0(SP) + // LW RA, 4(SP) + // ADDIU SP, SP, 8 + // + // We add 52 bytes to t9 because we want to adjust the function pointer to + // the actual start of function i.e. the address just after the noop sled. + // We do this because gp displacement relocation is emitted at the start of + // of the function i.e after the nop sled and to correctly calculate the + // global offset table address, t9 must hold the address of the instruction + // containing the gp displacement relocation. + // FIXME: Is this correct for the static relocation model? + // + // For mips64 we want to emit the following pattern: + // + // .Lxray_sled_N: + // ALIGN + // B .tmpN + // 15 NOP instructions (60 bytes) + // .tmpN + // + // We need the 60 bytes (15 instructions) because at runtime, we'd + // be patching over the full 64 bytes (16 instructions) with the following + // pattern: + // + // DADDIU SP, SP, -16 + // NOP + // SD RA, 8(SP) + // SD T9, 0(SP) + // LUI T9, %highest(__xray_FunctionEntry/Exit) + // ORI T9, T9, %higher(__xray_FunctionEntry/Exit) + // DSLL T9, T9, 16 + // ORI T9, T9, %hi(__xray_FunctionEntry/Exit) + // DSLL T9, T9, 16 + // ORI T9, T9, %lo(__xray_FunctionEntry/Exit) + // LUI T0, %hi(function_id) + // JALR T9 + // ADDIU T0, T0, %lo(function_id) + // LD T9, 0(SP) + // LD RA, 8(SP) + // DADDIU SP, SP, 16 + // + OutStreamer->EmitCodeAlignment(4); + auto CurSled = OutContext.createTempSymbol("xray_sled_", true); + OutStreamer->EmitLabel(CurSled); + auto Target = OutContext.createTempSymbol(); + + // Emit "B .tmpN" instruction, which jumps over the nop sled to the actual + // start of function + const MCExpr *TargetExpr = MCSymbolRefExpr::create( + Target, MCSymbolRefExpr::VariantKind::VK_None, OutContext); + EmitToStreamer(*OutStreamer, MCInstBuilder(Mips::BEQ) + .addReg(Mips::ZERO) + .addReg(Mips::ZERO) + .addExpr(TargetExpr)); + + for (int8_t I = 0; I < NoopsInSledCount; I++) + EmitToStreamer(*OutStreamer, MCInstBuilder(Mips::SLL) + .addReg(Mips::ZERO) + .addReg(Mips::ZERO) + .addImm(0)); + + OutStreamer->EmitLabel(Target); + + if (!Subtarget->isGP64bit()) { + EmitToStreamer(*OutStreamer, + MCInstBuilder(Mips::ADDiu) + .addReg(Mips::T9) + .addReg(Mips::T9) + .addImm(0x34)); + } + + recordSled(CurSled, MI, Kind); +} + +void MipsAsmPrinter::EmitXRayTable() { + if (Sleds.empty()) + return; + if (Subtarget->isTargetELF()) { + auto PrevSection = OutStreamer->getCurrentSectionOnly(); + auto Fn = MF->getFunction(); + MCSection *Section; + + if (Fn->hasComdat()) + Section = OutContext.getELFSection("xray_instr_map", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_GROUP, 0, + Fn->getComdat()->getName()); + else + Section = + OutContext.getELFSection("xray_instr_map", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC, 0, CurrentFnSym->getName()); + + OutStreamer->SwitchSection(Section); + for (const auto &Sled : Sleds) { + OutStreamer->EmitSymbolValue(Sled.Sled, Subtarget->isGP64bit() ? 8 : 4); + OutStreamer->EmitSymbolValue(CurrentFnSym, Subtarget->isGP64bit() ? 8 : 4); + auto Kind = static_cast(Sled.Kind); + OutStreamer->EmitBytes( + StringRef(reinterpret_cast(&Kind), 1)); + OutStreamer->EmitBytes( + StringRef(reinterpret_cast(&Sled.AlwaysInstrument), 1)); + OutStreamer->EmitZeros(Subtarget->isGP64bit() ? 14 : 6); + } + OutStreamer->SwitchSection(PrevSection); + } + Sleds.clear(); +} + +void MipsAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI) { + EmitSled(MI, SledKind::FUNCTION_ENTER); +} + +void MipsAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) { + EmitSled(MI, SledKind::FUNCTION_EXIT); +} + +void MipsAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) { + EmitSled(MI, SledKind::TAIL_CALL); +} + void MipsAsmPrinter::PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS) { // TODO: implement Index: lib/Target/Mips/MipsSubtarget.h =================================================================== --- lib/Target/Mips/MipsSubtarget.h +++ lib/Target/Mips/MipsSubtarget.h @@ -236,6 +236,7 @@ return (HasSym32 && isABI_N64()) || isABI_N32() || isABI_O32(); } bool isSingleFloat() const { return IsSingleFloat; } + bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); } bool hasVFPU() const { return HasVFPU; } bool inMips16Mode() const { return InMips16Mode; } bool inMips16ModeDefault() const { @@ -277,6 +278,8 @@ bool isTargetNaCl() const { return TargetTriple.isOSNaCl(); } + bool isXRaySupported() const override { return true; } + // for now constant islands are on for the whole compilation unit but we only // really use them if in addition we are in mips16 mode static bool useConstantIslands(); Index: test/CodeGen/Mips/xray-mips-attribute-instrumentation.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/xray-mips-attribute-instrumentation.ll @@ -0,0 +1,147 @@ +; RUN: llc -filetype=asm -o - -mtriple=mips-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS32 %s +; RUN: llc -filetype=asm -o - -mtriple=mipsel-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS32 %s +; RUN: llc -filetype=asm -o - -mtriple=mips64-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS64 %s +; RUN: llc -filetype=asm -o - -mtriple=mips64el-unknown-linux-gnu < %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MIPS64 %s + +define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" { +; CHECK: .p2align 2 +; CHECK-MIPS64-LABEL: .Lxray_sled_0: +; CHECK-MIPS32-LABEL: $xray_sled_0: +; CHECK-MIPS64: b .Ltmp0 +; CHECK-MIPS32: b $tmp0 +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64-LABEL: .Ltmp0: +; CHECK-MIPS32-LABEL: $tmp0: +; CHECK-MIPS32: addiu $25, $25, 52 + ret i32 0 +; CHECK: .p2align 2 +; CHECK-MIPS64-LABEL: .Lxray_sled_1: +; CHECK-MIPS32-LABEL: $xray_sled_1: +; CHECK-MIPS64: b .Ltmp1 +; CHECK-MIPS32: b $tmp1 +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64-LABEL: .Ltmp1: +; CHECK-MIPS32-LABEL: $tmp1: +; CHECK-MIPS32: addiu $25, $25, 52 +} +; CHECK: .section xray_instr_map,{{.*}} +; CHECK-MIPS64: .8byte .Lxray_sled_0 +; CHECK-MIPS64: .8byte .Lxray_sled_1 +; CHECK-MIPS32: .4byte ($xray_sled_0) +; CHECK-MIPS32: .4byte ($xray_sled_1) + +; We test multiple returns in a single function to make sure we're getting all +; of them with XRay instrumentation. +define i32 @bar(i32 %i) nounwind noinline uwtable "function-instrument"="xray-always" { +; CHECK: .p2align 2 +; CHECK-MIPS64-LABEL: .Lxray_sled_2: +; CHECK-MIPS32-LABEL: $xray_sled_2: +; CHECK-MIPS64: b .Ltmp2 +; CHECK-MIPS32: b $tmp2 +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64-LABEL: .Ltmp2: +; CHECK-MIPS32-LABEL: $tmp2: +; CHECK-MIPS32: addiu $25, $25, 52 +Test: + %cond = icmp eq i32 %i, 0 + br i1 %cond, label %IsEqual, label %NotEqual +IsEqual: + ret i32 0 +; CHECK: .p2align 2 +; CHECK-MIPS64-LABEL: .Lxray_sled_3: +; CHECK-MIPS32-LABEL: $xray_sled_3: +; CHECK-MIPS64: b .Ltmp3 +; CHECK-MIPS32: b $tmp3 +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64-LABEL: .Ltmp3: +; CHECK-MIPS32-LABEL: $tmp3: +; CHECK-MIPS32: addiu $25, $25, 52 +NotEqual: + ret i32 1 +; CHECK: .p2align 2 +; CHECK-MIPS64-LABEL: .Lxray_sled_4: +; CHECK-MIPS32-LABEL: $xray_sled_4: +; CHECK-MIPS64: b .Ltmp4 +; CHECK-MIPS32: b $tmp4 +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64: nop +; CHECK-MIPS64-LABEL: .Ltmp4: +; CHECK-MIPS32-LABEL: $tmp4: +; CHECK-MIPS32: addiu $25, $25, 52 +} +; CHECK: .section xray_instr_map,{{.*}} +; CHECK-MIPS64: .8byte .Lxray_sled_2 +; CHECK-MIPS64: .8byte .Lxray_sled_3 +; CHECK-MIPS64: .8byte .Lxray_sled_4 +; CHECK-MIPS32: .4byte ($xray_sled_2) +; CHECK-MIPS32: .4byte ($xray_sled_3) +; CHECK-MIPS32: .4byte ($xray_sled_4) Index: test/CodeGen/Mips/xray-section-group.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/xray-section-group.ll @@ -0,0 +1,31 @@ +; RUN: llc -filetype=asm -o - -mtriple=mips-unknown-linux-gnu -function-sections < %s | FileCheck %s +; RUN: llc -filetype=asm -o - -mtriple=mipsel-unknown-linux-gnu -function-sections < %s | FileCheck %s +; RUN: llc -filetype=obj -o %t -mtriple=mips-unknown-linux-gnu -function-sections < %s +; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ +; RUN: llc -filetype=obj -o %t -mtriple=mipsel-unknown-linux-gnu -function-sections < %s +; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ +; RUN: llc -filetype=asm -o - -mtriple=mips64-unknown-linux-gnu -function-sections < %s | FileCheck %s +; RUN: llc -filetype=asm -o - -mtriple=mips64el-unknown-linux-gnu -function-sections < %s | FileCheck %s +; RUN: llc -filetype=obj -o %t -mtriple=mips64-unknown-linux-gnu -function-sections < %s +; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ +; RUN: llc -filetype=obj -o %t -mtriple=mips64el-unknown-linux-gnu -function-sections < %s +; RUN: llvm-readobj -sections %t | FileCheck %s --check-prefix=CHECK-OBJ + +define i32 @foo() nounwind noinline uwtable "function-instrument"="xray-always" { +; CHECK: .section .text.foo,"ax",@progbits + ret i32 0 +; CHECK: .section xray_instr_map,"a",@progbits +} + +; CHECK-OBJ: Section { +; CHECK-OBJ: Name: xray_instr_map + +$bar = comdat any +define i32 @bar() nounwind noinline uwtable "function-instrument"="xray-always" comdat($bar) { +; CHECK: .section .text.bar,"axG",@progbits,bar,comdat + ret i32 1 +; CHECK: .section xray_instr_map,"aG",@progbits,bar,comdat +} + +; CHECK-OBJ: Section { +; CHECK-OBJ: Name: xray_instr_map