Files
TheRock/compiler/amd-llvm/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCLFIRewriter.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

291 lines
9.5 KiB
C++

//===- AArch64MCLFIRewriter.cpp ---------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the AArch64MCLFIRewriter class, the AArch64 specific
// subclass of MCLFIRewriter.
//
//===----------------------------------------------------------------------===//
#include "AArch64MCLFIRewriter.h"
#include "AArch64AddressingModes.h"
#include "MCTargetDesc/AArch64MCTargetDesc.h"
#include "Utils/AArch64BaseInfo.h"
#include "llvm/ADT/Twine.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
using namespace llvm;
// LFI reserved registers.
static constexpr MCRegister LFIBaseReg = AArch64::X27;
static constexpr MCRegister LFIAddrReg = AArch64::X28;
static constexpr MCRegister LFIScratchReg = AArch64::X26;
static constexpr MCRegister LFICtxReg = AArch64::X25;
// Offset into the context register block (pointed to by LFICtxReg) where the
// thread pointer is stored. This is a scaled offset (multiplied by 8 for
// 64-bit loads), so a value of 2 means an actual byte offset of 16.
static constexpr unsigned LFITPOffset = 2;
// Byte offset from the sandbox base register where the syscall handler address
// is stored (negative because it is below the sandbox base).
static constexpr int LFISyscallOffset = -8;
static bool isSyscall(const MCInst &Inst) {
return Inst.getOpcode() == AArch64::SVC;
}
static bool isPrivilegedTP(int64_t Reg) {
return Reg == AArch64SysReg::TPIDR_EL1 || Reg == AArch64SysReg::TPIDR_EL2 ||
Reg == AArch64SysReg::TPIDR_EL3;
}
static bool isTPRead(const MCInst &Inst) {
return Inst.getOpcode() == AArch64::MRS &&
Inst.getOperand(1).getImm() == AArch64SysReg::TPIDR_EL0;
}
static bool isTPWrite(const MCInst &Inst) {
return Inst.getOpcode() == AArch64::MSR &&
Inst.getOperand(0).getImm() == AArch64SysReg::TPIDR_EL0;
}
static bool isPrivilegedTPAccess(const MCInst &Inst) {
if (Inst.getOpcode() == AArch64::MRS)
return isPrivilegedTP(Inst.getOperand(1).getImm());
if (Inst.getOpcode() == AArch64::MSR)
return isPrivilegedTP(Inst.getOperand(0).getImm());
return false;
}
MCRegister AArch64MCLFIRewriter::mayModifyReserved(const MCInst &Inst) const {
for (MCRegister Reg : {LFIAddrReg, LFIBaseReg, LFICtxReg}) {
if (mayModifyRegister(Inst, Reg))
return Reg;
}
return {};
}
void AArch64MCLFIRewriter::emitInst(const MCInst &Inst, MCStreamer &Out,
const MCSubtargetInfo &STI) {
Out.emitInstruction(Inst, STI);
}
void AArch64MCLFIRewriter::emitAddMask(MCRegister Dest, MCRegister Src,
MCStreamer &Out,
const MCSubtargetInfo &STI) {
// add Dest, LFIBaseReg, W(Src), uxtw
MCInst Inst;
Inst.setOpcode(AArch64::ADDXrx);
Inst.addOperand(MCOperand::createReg(Dest));
Inst.addOperand(MCOperand::createReg(LFIBaseReg));
Inst.addOperand(MCOperand::createReg(getWRegFromXReg(Src)));
Inst.addOperand(
MCOperand::createImm(AArch64_AM::getArithExtendImm(AArch64_AM::UXTW, 0)));
emitInst(Inst, Out, STI);
}
void AArch64MCLFIRewriter::emitBranch(unsigned Opcode, MCRegister Target,
MCStreamer &Out,
const MCSubtargetInfo &STI) {
MCInst Branch;
Branch.setOpcode(Opcode);
Branch.addOperand(MCOperand::createReg(Target));
emitInst(Branch, Out, STI);
}
void AArch64MCLFIRewriter::emitPendingTLSDescCall(MCStreamer &Out,
const MCSubtargetInfo &STI) {
if (!PendingTLSDescCall)
return;
MCInst Marker;
Marker.setOpcode(AArch64::TLSDESCCALL);
Marker.addOperand(MCOperand::createExpr(PendingTLSDescCall));
PendingTLSDescCall = nullptr;
emitInst(Marker, Out, STI);
}
void AArch64MCLFIRewriter::emitMov(MCRegister Dest, MCRegister Src,
MCStreamer &Out,
const MCSubtargetInfo &STI) {
// orr Dest, xzr, Src
MCInst Inst;
Inst.setOpcode(AArch64::ORRXrs);
Inst.addOperand(MCOperand::createReg(Dest));
Inst.addOperand(MCOperand::createReg(AArch64::XZR));
Inst.addOperand(MCOperand::createReg(Src));
Inst.addOperand(MCOperand::createImm(0));
emitInst(Inst, Out, STI);
}
// {br,blr} xN
// ->
// add x28, x27, wN, uxtw
// {br,blr} x28
void AArch64MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
MCStreamer &Out,
const MCSubtargetInfo &STI) {
assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
"expected register operand");
MCRegister BranchReg = Inst.getOperand(0).getReg();
// Guard the branch target through X28.
emitAddMask(LFIAddrReg, BranchReg, Out, STI);
emitPendingTLSDescCall(Out, STI);
emitBranch(Inst.getOpcode(), LFIAddrReg, Out, STI);
}
// ret xN (where xN != x30)
// ->
// add x28, x27, wN, uxtw
// ret x28
//
// ret (x30) is safe since x30 is always within the sandbox.
void AArch64MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
const MCSubtargetInfo &STI) {
assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
"expected register operand");
// RET through LR is safe since LR is always within sandbox.
if (Inst.getOperand(0).getReg() != AArch64::LR)
rewriteIndirectBranch(Inst, Out, STI);
else
emitInst(Inst, Out, STI);
}
// modify x30
// ->
// modify x30
// add x30, x27, w30, uxtw
void AArch64MCLFIRewriter::rewriteLRModification(const MCInst &Inst,
MCStreamer &Out,
const MCSubtargetInfo &STI) {
emitInst(Inst, Out, STI);
emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
}
// svc #0
// ->
// mov x26, x30
// ldur x30, [x27, #-8]
// blr x30
// add x30, x27, w26, uxtw
void AArch64MCLFIRewriter::rewriteSyscall(const MCInst &, MCStreamer &Out,
const MCSubtargetInfo &STI) {
// Save LR to scratch.
emitMov(LFIScratchReg, AArch64::LR, Out, STI);
// Load syscall handler address from negative offset from sandbox base.
MCInst Load;
Load.setOpcode(AArch64::LDURXi);
Load.addOperand(MCOperand::createReg(AArch64::LR));
Load.addOperand(MCOperand::createReg(LFIBaseReg));
Load.addOperand(MCOperand::createImm(LFISyscallOffset));
emitInst(Load, Out, STI);
// Call the runtime.
emitBranch(AArch64::BLR, AArch64::LR, Out, STI);
// Restore LR with guard.
emitAddMask(AArch64::LR, LFIScratchReg, Out, STI);
}
// mrs xN, tpidr_el0
// ->
// ldr xN, [x25, #16]
void AArch64MCLFIRewriter::rewriteTPRead(const MCInst &Inst, MCStreamer &Out,
const MCSubtargetInfo &STI) {
MCRegister DestReg = Inst.getOperand(0).getReg();
MCInst Load;
Load.setOpcode(AArch64::LDRXui);
Load.addOperand(MCOperand::createReg(DestReg));
Load.addOperand(MCOperand::createReg(LFICtxReg));
Load.addOperand(MCOperand::createImm(LFITPOffset));
emitInst(Load, Out, STI);
}
// msr tpidr_el0, xN
// ->
// str xN, [x25, #16]
void AArch64MCLFIRewriter::rewriteTPWrite(const MCInst &Inst, MCStreamer &Out,
const MCSubtargetInfo &STI) {
MCRegister SrcReg = Inst.getOperand(1).getReg();
MCInst Store;
Store.setOpcode(AArch64::STRXui);
Store.addOperand(MCOperand::createReg(SrcReg));
Store.addOperand(MCOperand::createReg(LFICtxReg));
Store.addOperand(MCOperand::createImm(LFITPOffset));
emitInst(Store, Out, STI);
}
// NOTE: when adding new rewrites, the size estimates in
// AArch64InstrInfo::getLFIInstSizeInBytes must be updated to match.
void AArch64MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
const MCSubtargetInfo &STI) {
if (Inst.getOpcode() == AArch64::TLSDESCCALL) {
PendingTLSDescCall = Inst.getOperand(0).getExpr();
return;
}
// Reserved register modification is an error.
if (MCRegister Reg = mayModifyReserved(Inst)) {
error(Inst, Twine("illegal modification of reserved LFI register ") +
RegInfo->getName(Reg));
return;
}
// System instructions.
if (isSyscall(Inst))
return rewriteSyscall(Inst, Out, STI);
if (isTPRead(Inst))
return rewriteTPRead(Inst, Out, STI);
if (isTPWrite(Inst))
return rewriteTPWrite(Inst, Out, STI);
if (isPrivilegedTPAccess(Inst)) {
error(Inst, "illegal access to privileged thread pointer register");
return;
}
// Control flow.
switch (Inst.getOpcode()) {
case AArch64::RET:
return rewriteReturn(Inst, Out, STI);
case AArch64::BR:
case AArch64::BLR:
return rewriteIndirectBranch(Inst, Out, STI);
}
// Link register modification.
if (explicitlyModifiesRegister(Inst, AArch64::LR))
return rewriteLRModification(Inst, Out, STI);
emitInst(Inst, Out, STI);
}
bool AArch64MCLFIRewriter::rewriteInst(const MCInst &Inst, MCStreamer &Out,
const MCSubtargetInfo &STI) {
// The guard prevents rewrite-recursion when we emit instructions from inside
// the rewriter (such instructions should not be rewritten).
if (!Enabled || Guard)
return false;
Guard = true;
doRewriteInst(Inst, Out, STI);
Guard = false;
return true;
}