252 lines · cpp
1//===-- Target.cpp ----------------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "../Target.h"9#include "AArch64.h"10#include "AArch64RegisterInfo.h"11#include "llvm/CodeGen/MachineInstrBuilder.h"12 13#if defined(__aarch64__) && defined(__linux__)14#include <sys/prctl.h> // For PR_PAC_* constants15#ifndef PR_PAC_APIAKEY16#define PR_PAC_APIAKEY (1UL << 0)17#endif18#ifndef PR_PAC_APIBKEY19#define PR_PAC_APIBKEY (1UL << 1)20#endif21#ifndef PR_PAC_APDAKEY22#define PR_PAC_APDAKEY (1UL << 2)23#endif24#ifndef PR_PAC_APDBKEY25#define PR_PAC_APDBKEY (1UL << 3)26#endif27#endif28 29#define GET_AVAILABLE_OPCODE_CHECKER30#include "AArch64GenInstrInfo.inc"31 32namespace llvm {33namespace exegesis {34 35static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) {36 switch (RegBitWidth) {37 case 32:38 return AArch64::MOVi32imm;39 case 64:40 return AArch64::MOVi64imm;41 }42 llvm_unreachable("Invalid Value Width");43}44 45// Generates instruction to load an immediate value into a register.46static MCInst loadImmediate(MCRegister Reg, unsigned RegBitWidth,47 const APInt &Value) {48 assert(Value.getBitWidth() <= RegBitWidth &&49 "Value must fit in the Register");50 return MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))51 .addReg(Reg)52 .addImm(Value.getZExtValue());53}54 55static MCInst loadZPRImmediate(MCRegister Reg, unsigned RegBitWidth,56 const APInt &Value) {57 assert(Value.getZExtValue() < (1 << 7) &&58 "Value must be in the range of the immediate opcode");59 return MCInstBuilder(AArch64::DUP_ZI_D)60 .addReg(Reg)61 .addImm(Value.getZExtValue())62 .addImm(0);63}64 65static MCInst loadPPRImmediate(MCRegister Reg, unsigned RegBitWidth,66 const APInt &Value) {67 // For PPR, we typically use PTRUE instruction to set predicate registers68 return MCInstBuilder(AArch64::PTRUE_B)69 .addReg(Reg)70 .addImm(31); // All lanes true for 16 bits71}72 73// Generates instructions to load an immediate value into an FPCR register.74static std::vector<MCInst>75loadFPCRImmediate(MCRegister Reg, unsigned RegBitWidth, const APInt &Value) {76 MCRegister TempReg = AArch64::X8;77 MCInst LoadImm = MCInstBuilder(AArch64::MOVi64imm).addReg(TempReg).addImm(0);78 MCInst MoveToFPCR =79 MCInstBuilder(AArch64::MSR).addImm(AArch64SysReg::FPCR).addReg(TempReg);80 return {LoadImm, MoveToFPCR};81}82 83// Fetch base-instruction to load an FP immediate value into a register.84static unsigned getLoadFPImmediateOpcode(unsigned RegBitWidth) {85 switch (RegBitWidth) {86 case 16:87 return AArch64::FMOVH0; // FMOVHi;88 case 32:89 return AArch64::FMOVS0; // FMOVSi;90 case 64:91 return AArch64::MOVID; // FMOVDi;92 case 128:93 return AArch64::MOVIv2d_ns;94 }95 llvm_unreachable("Invalid Value Width");96}97 98// Generates instruction to load an FP immediate value into a register.99static MCInst loadFPImmediate(MCRegister Reg, unsigned RegBitWidth,100 const APInt &Value) {101 assert(Value.getZExtValue() == 0 && "Expected initialisation value 0");102 MCInst Instructions =103 MCInstBuilder(getLoadFPImmediateOpcode(RegBitWidth)).addReg(Reg);104 if (RegBitWidth >= 64)105 Instructions.addOperand(MCOperand::createImm(Value.getZExtValue()));106 return Instructions;107}108 109#include "AArch64GenExegesis.inc"110 111namespace {112 113// Use X19 as the loop counter register since it's a callee-saved register114// that's available for temporary use.115constexpr MCPhysReg kDefaultLoopCounterReg = AArch64::X19;116 117class ExegesisAArch64Target : public ExegesisTarget {118public:119 ExegesisAArch64Target()120 : ExegesisTarget(AArch64CpuPfmCounters, AArch64_MC::isOpcodeAvailable) {}121 122 Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var,123 MCOperand &AssignedValue,124 const BitVector &ForbiddenRegs) const override;125 126private:127 std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, MCRegister Reg,128 const APInt &Value) const override {129 if (AArch64::GPR32RegClass.contains(Reg))130 return {loadImmediate(Reg, 32, Value)};131 if (AArch64::GPR64RegClass.contains(Reg))132 return {loadImmediate(Reg, 64, Value)};133 if (AArch64::PPRRegClass.contains(Reg))134 return {loadPPRImmediate(Reg, 16, Value)};135 if (AArch64::FPR8RegClass.contains(Reg))136 return {loadFPImmediate(Reg - AArch64::B0 + AArch64::D0, 64, Value)};137 if (AArch64::FPR16RegClass.contains(Reg))138 return {loadFPImmediate(Reg, 16, Value)};139 if (AArch64::FPR32RegClass.contains(Reg))140 return {loadFPImmediate(Reg, 32, Value)};141 if (AArch64::FPR64RegClass.contains(Reg))142 return {loadFPImmediate(Reg, 64, Value)};143 if (AArch64::FPR128RegClass.contains(Reg))144 return {loadFPImmediate(Reg, 128, Value)};145 if (AArch64::ZPRRegClass.contains(Reg))146 return {loadZPRImmediate(Reg, 128, Value)};147 if (Reg == AArch64::FPCR)148 return {loadFPCRImmediate(Reg, 32, Value)};149 150 errs() << "setRegTo is not implemented, results will be unreliable\n";151 return {};152 }153 MCRegister getDefaultLoopCounterRegister(const Triple &) const override {154 return kDefaultLoopCounterReg;155 }156 157 void decrementLoopCounterAndJump(MachineBasicBlock &MBB,158 MachineBasicBlock &TargetMBB,159 const MCInstrInfo &MII,160 MCRegister LoopRegister) const override {161 // subs LoopRegister, LoopRegister, #1162 BuildMI(&MBB, DebugLoc(), MII.get(AArch64::SUBSXri))163 .addDef(LoopRegister)164 .addUse(LoopRegister)165 .addImm(1) // Subtract 1166 .addImm(0); // No shift amount167 // b.ne TargetMBB168 BuildMI(&MBB, DebugLoc(), MII.get(AArch64::Bcc))169 .addImm(AArch64CC::NE)170 .addMBB(&TargetMBB);171 }172 173 // Registers that should not be selected for use in snippets.174 const MCPhysReg UnavailableRegisters[1] = {kDefaultLoopCounterReg};175 ArrayRef<MCPhysReg> getUnavailableRegisters() const override {176 return UnavailableRegisters;177 }178 179 bool matchesArch(Triple::ArchType Arch) const override {180 return Arch == Triple::aarch64 || Arch == Triple::aarch64_be;181 }182 183 void addTargetSpecificPasses(PassManagerBase &PM) const override {184 // Function return is a pseudo-instruction that needs to be expanded185 PM.add(createAArch64ExpandPseudoPass());186 }187};188 189Error ExegesisAArch64Target::randomizeTargetMCOperand(190 const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue,191 const BitVector &ForbiddenRegs) const {192 const Operand &Op = Instr.getPrimaryOperand(Var);193 const auto OperandType = Op.getExplicitOperandInfo().OperandType;194 // NOTE: To resolve "Not all operands were initialized by snippet generator"195 // Requires OperandType to be defined for such opcode's operands in AArch64196 // tablegen files. And omit introduced OperandType(s).197 198 // Hacky Fix: Defaulting all OPERAND_UNKNOWN to immediate value 0 works with a199 // limitation that it introduces illegal instruction error for system200 // instructions. System instructions will need to be omitted with OperandType201 // or opcode specific values to avoid generating invalid encodings or202 // unreliable benchmark results for these system-level instructions.203 // Implement opcode-specific immediate value handling for system instrs:204 // - MRS/MSR: Use valid system register encodings (e.g., NZCV, FPCR, FPSR)205 // - MSRpstatesvcrImm1: Use valid PSTATE field encodings (e.g., SPSel,206 // DAIFSet)207 // - SYSLxt/SYSxt: Use valid system instruction encodings with proper208 // CRn/CRm/op values209 // - UDF: Use valid undefined instruction immediate ranges (0-65535)210 211 switch (OperandType) {212 // MSL (Masking Shift Left) imm operand for 32-bit splatted SIMD constants213 // Correspond to AArch64InstructionSelector::tryAdvSIMDModImm321s()214 case llvm::AArch64::OPERAND_SHIFT_MSL: {215 // There are two valid encodings:216 // - Type 7: imm at [15:8], [47:40], shift = 264 (0x108) → msl #8217 // - Type 8: imm at [23:16], [55:48], shift = 272 (0x110) → msl #16218 // Corresponds AArch64_AM::encodeAdvSIMDModImmType7()219 // But, v2s_msl and v4s_msl instructions accept either form,220 // Thus, Arbitrarily chosing 264 (msl #8) for simplicity.221 AssignedValue = MCOperand::createImm(264);222 return Error::success();223 }224 case llvm::AArch64::OPERAND_IMPLICIT_IMM_0:225 AssignedValue = MCOperand::createImm(0);226 return Error::success();227 case MCOI::OperandType::OPERAND_PCREL:228 AssignedValue = MCOperand::createImm(8);229 return Error::success();230 default:231 break;232 }233 234 return make_error<Failure>(235 Twine("Unimplemented operand type: MCOI::OperandType:")236 .concat(Twine(static_cast<int>(OperandType))));237}238 239} // namespace240 241static ExegesisTarget *getTheExegesisAArch64Target() {242 static ExegesisAArch64Target Target;243 return &Target;244}245 246void InitializeAArch64ExegesisTarget() {247 ExegesisTarget::registerTarget(getTheExegesisAArch64Target());248}249 250} // namespace exegesis251} // namespace llvm252