Index: include/llvm/CodeGen/AsmPrinter.h =================================================================== --- include/llvm/CodeGen/AsmPrinter.h +++ include/llvm/CodeGen/AsmPrinter.h @@ -200,6 +200,7 @@ FUNCTION_ENTER = 0, FUNCTION_EXIT = 1, TAIL_CALL = 2, + CUSTOM_EVENT = 3, }; // The table will contain these structs that point to the sled, the function Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -772,6 +772,14 @@ def int_load_relative: Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty, llvm_anyint_ty], [IntrReadMem, IntrArgMemOnly]>; +// Xray intrinsics +//===----------------------------------------------------------------------===// +// Custom event logging for x-ray. +// Takes a pointer to a string and the length of the string. +def int_xray_customevent : Intrinsic<[], [llvm_ptr_ty, llvm_i8_ty], + [IntrReadMem, IntrArgMemOnly]>; +//===----------------------------------------------------------------------===// + //===------ Memory intrinsics with element-wise atomicity guarantees ------===// // Index: include/llvm/Target/Target.td =================================================================== --- include/llvm/Target/Target.td +++ include/llvm/Target/Target.td @@ -998,6 +998,16 @@ let hasSideEffects = 1; let isReturn = 1; } +def PATCHABLE_EVENT_CALL : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins unknown:$event, i8imm:$size); + let AsmString = "# XRay Custom Event Log."; + let usesCustomInserter = 1; + let isCall = 1; + let mayLoad = 1; + let mayStore = 1; + let hasSideEffects = 1; +} // Generic opcodes used in GlobalISel. include "llvm/Target/GenericOpcodes.td" Index: include/llvm/Target/TargetOpcodes.def =================================================================== --- include/llvm/Target/TargetOpcodes.def +++ include/llvm/Target/TargetOpcodes.def @@ -177,6 +177,10 @@ /// PATCHABLE_RET which specifically only works for return instructions. HANDLE_TARGET_OPCODE(PATCHABLE_TAIL_CALL) +/// Wraps a logging call and its arguments with nop sleds. At runtime, this can be +/// patched to insert instrumentation instructions. +HANDLE_TARGET_OPCODE(PATCHABLE_EVENT_CALL) + /// The following generic opcodes are not supposed to appear after ISel. /// This is something we might want to relax, but for now, this is convenient /// to produce diagnostics. Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5784,7 +5784,23 @@ setValue(&I, N); return nullptr; } - + case Intrinsic::xray_customevent: { + SDLoc dl = getCurSDLoc(); + SmallVector Ops; + SDValue LogEntryVal = getValue(I.getArgOperand(0)); + SDValue StrSizeVal = getValue(I.getArgOperand(1)); + + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + SDValue Chain = getRoot(); + Ops.push_back(LogEntryVal); + Ops.push_back(StrSizeVal); + Ops.push_back(Chain); + MachineSDNode *MN = DAG.getMachineNode(TargetOpcode::PATCHABLE_EVENT_CALL, dl, NodeTys, Ops); + SDValue patchableNode = SDValue(MN, 0); + DAG.setRoot(patchableNode); + setValue(&I, patchableNode); + return nullptr; + } case Intrinsic::experimental_deoptimize: LowerDeoptimizeCall(&I); return nullptr; Index: lib/Target/X86/X86AsmPrinter.h =================================================================== --- lib/Target/X86/X86AsmPrinter.h +++ lib/Target/X86/X86AsmPrinter.h @@ -91,6 +91,7 @@ X86MCInstLower &MCIL); void LowerPATCHABLE_RET(const MachineInstr &MI, X86MCInstLower &MCIL); void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI, X86MCInstLower &MCIL); + void LowerPATCHABLE_EVENT_CALL(const MachineInstr &MI, X86MCInstLower &MCIL); // Helper function that emits the XRay sleds we've collected for a particular // function. Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -26198,6 +26198,10 @@ case TargetOpcode::STACKMAP: case TargetOpcode::PATCHPOINT: return emitPatchPoint(MI, BB); + + case TargetOpcode::PATCHABLE_EVENT_CALL: + // Do nothing here, handle in xray instrumentation pass. + return BB; case X86::LCMPXCHG8B: { const X86RegisterInfo *TRI = Subtarget.getRegisterInfo(); Index: lib/Target/X86/X86MCInstLower.cpp =================================================================== --- lib/Target/X86/X86MCInstLower.cpp +++ lib/Target/X86/X86MCInstLower.cpp @@ -1022,6 +1022,87 @@ getSubtargetInfo()); } +void X86AsmPrinter::LowerPATCHABLE_EVENT_CALL(const MachineInstr &MI, + X86MCInstLower &MCIL) { + // We want to emit the following pattern, which follows the x86 calling convention + // to prepare for the trampoline call to be patched in. + // + // .p2align 1, ... + // .Lxray_event_sled_N: + // jmp .tmpN # 2 bytes + // # 4 bytes of noops (reserve space for mov instruction) + // [3 times] push (15 bytes = 3 registers * 5 bytes) + // push (5 bytes) + // push (5 bytes) + // # 5 bytes of noops (reserve space for call) + // addq, $8, esp (9 bytes, clean parameters) + // [3 times] pop (15 bytes) + // .tmpN + // + // The relative jump needs to jump forward 49 bytes: + // 4 (nops) + 15 (caller-saved) + 10 (args) + 5 (nops) + 9 + 15 + // + // After patching, it would look something like: + // + // mov %r10, // 6 bytes + // + // push // 5 bytes + // call // 5 bytes + // + // + auto CurSled = OutContext.createTempSymbol("xray_event_sled_", true); + OutStreamer->EmitCodeAlignment(2); + OutStreamer->EmitLabel(CurSled); + auto Target = OutContext.createTempSymbol(); + + // FIXME: I used PUSH64r etc but does this need to be parameterized somehow based on the target? + + // Use a two-byte `jmp`. This version of JMP takes an 8-bit relative offset as + // an operand (computed as an offset from the jmp instruction). + // FIXME: Find another less hacky way do force the relative jump. + OutStreamer->EmitBytes("\xeb\x31"); + EmitNops(*OutStreamer, 4, Subtarget->is64Bit(), getSubtargetInfo()); + + // Push caller-saved registers per x86 calling convention. + static const unsigned CallerSavedReg[3] = {X86::EAX, X86::ECX, X86::EDX}; + for (unsigned int i = 0; i < sizeof(CallerSavedReg) / sizeof(*CallerSavedReg); ++i) { + MCInst PushCallerSaved = MCInstBuilder(X86::PUSH64r) + .addOperand(MCOperand::createReg(CallerSavedReg[i])); + OutStreamer->EmitInstruction(PushCallerSaved, getSubtargetInfo()); + } + + // Push the arguments in reverse order per x86 calling convention. + // FIXME(louisli): this is not in reverse -- what's the elegant way to reverse this iterator? + for (auto &MO : make_range(MI.operands_begin(), MI.operands_end())) { + if (auto MaybeOperand = MCIL.LowerMachineOperand(&MI, MO)) { + MCInst PushLength = MCInstBuilder(X86::PUSH64r) + .addOperand(MaybeOperand.getValue()); + OutStreamer->EmitInstruction(PushLength, getSubtargetInfo()); + } + } + + // nops to be patched with a trampoline call. + EmitNops(*OutStreamer, 5, Subtarget->is64Bit(), getSubtargetInfo()); + + // ESP - (2 parameters * 4 bytes) + // FIXME(louisli): There are many types of ADD instructions, not sure which one to use. + MCInst RemoveParameters = MCInstBuilder(X86::ADD64ri8) + .addOperand(MCOperand::createReg(X86::ESP)) + .addOperand(MCOperand::createReg(X86::ESP)) + .addOperand(MCOperand::createImm(8)); + OutStreamer->EmitInstruction(RemoveParameters, getSubtargetInfo()); + + // Restore caller saved registers (popping in reverse order) + for (int i = sizeof(CallerSavedReg) / sizeof(*CallerSavedReg) - 1; i >= 0; --i) { + MCInst PopCallerSaved = MCInstBuilder(X86::POP64r) + .addOperand(MCOperand::createReg(CallerSavedReg[i])); + OutStreamer->EmitInstruction(PopCallerSaved, getSubtargetInfo()); + } + + OutStreamer->EmitLabel(Target); + recordSled(CurSled, MI, SledKind::CUSTOM_EVENT); +} + void X86AsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI, X86MCInstLower &MCIL) { // We want to emit the following pattern: @@ -1394,6 +1475,9 @@ case TargetOpcode::PATCHABLE_TAIL_CALL: return LowerPATCHABLE_TAIL_CALL(*MI, MCInstLowering); + + case TargetOpcode::PATCHABLE_EVENT_CALL: + return LowerPATCHABLE_EVENT_CALL(*MI, MCInstLowering); case X86::MORESTACK_RET: EmitAndCountInstruction(MCInstBuilder(getRetOpcode(*Subtarget))); Index: test/CodeGen/X86/xray-custom-log.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/xray-custom-log.ll @@ -0,0 +1,26 @@ +; RUN: llc -filetype=asm -o - -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s + +define i32 @fn() nounwind noinline uwtable "function-instrument"="xray-always" { + %eventptr = alloca i8 + %eventsize = alloca i8 + store i8 3, i8* %eventsize + %val = load i8, i8* %eventsize + call void @llvm.xray.customevent(i8* %eventptr, i8 %val) + ; CHECK-LABEL: .Lxray_event_sled_0: + ; CHECK-NEXT: .ascii "\3531" + ; CHECK-NEXT: nopl 8(%rax) + ; CHECK-NEXT: pushq %eax + ; CHECK-NEXT: pushq %ecx + ; CHECK-NEXT: pushq %edx + ; CHECK-NEXT: pushq %rcx + ; CHECK-NEXT: pushq %al + ; CHECK-NEXT: nopl 8(%rax,%rax) + ; CHECK-NEXT: addq $8, %esp + ; CHECK-NEXT: popq %edx + ; CHECK-NEXT: popq %ecx + ; CHECK-NEXT: popq %eax + ret i32 0 +} + +declare void @llvm.xray.customevent(i8*, i8) +