Index: include/llvm/CodeGen/AsmPrinter.h =================================================================== --- include/llvm/CodeGen/AsmPrinter.h +++ include/llvm/CodeGen/AsmPrinter.h @@ -222,6 +222,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 @@ -999,6 +999,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; +} def FENTRY_CALL : Instruction { let OutOperandList = (outs unknown:$dst); let InOperandList = (ins variable_ops); Index: include/llvm/Target/TargetOpcodes.def =================================================================== --- include/llvm/Target/TargetOpcodes.def +++ include/llvm/Target/TargetOpcodes.def @@ -182,6 +182,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 @@ -5764,7 +5764,22 @@ 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); void LowerFENTRY_CALL(const MachineInstr &MI, X86MCInstLower &MCIL); Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -26242,6 +26242,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 @@ -1040,6 +1040,61 @@ 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 + // push (5 bytes) + // push (5 bytes) + // # 5 bytes of noops (reservespace for call) + // addq, $8, esp (9 bytes, clean parameters) + // .tmpN + // + // The relative jump needs to jump forward 24 bytes: + // 10 (args) + 5 (nops) + 9 (cleanup) + // + // After patching, it would look something like: + // + // nopw (2-byte nop) + // push // 5 bytes + // push // 5 bytes + // call // 5 bytes + // addq, $8, esp // 9 bytes + // + auto CurSled = OutContext.createTempSymbol("xray_event_sled_", true); + OutStreamer->EmitCodeAlignment(2); + OutStreamer->EmitLabel(CurSled); + auto Target = OutContext.createTempSymbol(); + + // 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\x18"); + + // Push the arguments in reverse order per x86 calling convention. + for (auto &MO : reverse(MI.operands())) + if (auto MaybeOperand = MCIL.LowerMachineOperand(&MI, MO)) + OutStreamer->EmitInstruction( + MCInstBuilder(X86::PUSH64r).addOperand(MaybeOperand.getValue()), + getSubtargetInfo()); + + // nops to be patched with a trampoline call. + EmitNops(*OutStreamer, 5, Subtarget->is64Bit(), getSubtargetInfo()); + + // ESP - (2 parameters * 4 bytes) + MCInst RemoveParameters = MCInstBuilder(X86::ADD64ri8) + .addOperand(MCOperand::createReg(X86::ESP)) + .addOperand(MCOperand::createReg(X86::ESP)) + .addOperand(MCOperand::createImm(8)); + OutStreamer->EmitInstruction(RemoveParameters, 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: @@ -1415,6 +1470,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,22 @@ +; 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 "\353\030 + ; CHECK-NEXT: pushq %al + ; CHECK-NEXT: pushq %rcx + ; CHECK-NEXT: nopl 8(%rax,%rax) + ; CHECK-NEXT: addq $8, %esp + ret i32 0 +} +; CHECK: .section {{.*}}xray_instr_map +; CHECK-LABEL: Lxray_synthetic_0: +; CHECK: .quad {{.*}}xray_event_sled_0 + +declare void @llvm.xray.customevent(i8*, i8) +