1 //===-- RISCVAsmPrinter.cpp - RISC-V LLVM assembly writer -----------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains a printer that converts from our internal representation 10 // of machine-dependent LLVM code to the RISC-V assembly language. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "MCTargetDesc/RISCVBaseInfo.h" 15 #include "MCTargetDesc/RISCVInstPrinter.h" 16 #include "MCTargetDesc/RISCVMCExpr.h" 17 #include "MCTargetDesc/RISCVMatInt.h" 18 #include "MCTargetDesc/RISCVTargetStreamer.h" 19 #include "RISCV.h" 20 #include "RISCVMachineFunctionInfo.h" 21 #include "RISCVTargetMachine.h" 22 #include "TargetInfo/RISCVTargetInfo.h" 23 #include "llvm/ADT/APInt.h" 24 #include "llvm/ADT/Statistic.h" 25 #include "llvm/BinaryFormat/ELF.h" 26 #include "llvm/CodeGen/AsmPrinter.h" 27 #include "llvm/CodeGen/MachineConstantPool.h" 28 #include "llvm/CodeGen/MachineFunctionPass.h" 29 #include "llvm/CodeGen/MachineInstr.h" 30 #include "llvm/CodeGen/MachineModuleInfo.h" 31 #include "llvm/IR/Module.h" 32 #include "llvm/MC/MCAsmInfo.h" 33 #include "llvm/MC/MCContext.h" 34 #include "llvm/MC/MCInst.h" 35 #include "llvm/MC/MCInstBuilder.h" 36 #include "llvm/MC/MCObjectFileInfo.h" 37 #include "llvm/MC/MCSectionELF.h" 38 #include "llvm/MC/MCStreamer.h" 39 #include "llvm/MC/MCSymbol.h" 40 #include "llvm/MC/TargetRegistry.h" 41 #include "llvm/Support/raw_ostream.h" 42 #include "llvm/TargetParser/RISCVISAInfo.h" 43 #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" 44 45 using namespace llvm; 46 47 #define DEBUG_TYPE "asm-printer" 48 49 STATISTIC(RISCVNumInstrsCompressed, 50 "Number of RISC-V Compressed instructions emitted"); 51 52 namespace llvm { 53 extern const SubtargetFeatureKV RISCVFeatureKV[RISCV::NumSubtargetFeatures]; 54 } // namespace llvm 55 56 namespace { 57 class RISCVAsmPrinter : public AsmPrinter { 58 const RISCVSubtarget *STI; 59 60 public: 61 explicit RISCVAsmPrinter(TargetMachine &TM, 62 std::unique_ptr<MCStreamer> Streamer) 63 : AsmPrinter(TM, std::move(Streamer)) {} 64 65 StringRef getPassName() const override { return "RISC-V Assembly Printer"; } 66 67 void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, 68 const MachineInstr &MI); 69 70 void LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, 71 const MachineInstr &MI); 72 73 void LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM, 74 const MachineInstr &MI); 75 76 bool runOnMachineFunction(MachineFunction &MF) override; 77 78 void emitInstruction(const MachineInstr *MI) override; 79 80 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, 81 const char *ExtraCode, raw_ostream &OS) override; 82 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, 83 const char *ExtraCode, raw_ostream &OS) override; 84 85 // Returns whether Inst is compressed. 86 bool EmitToStreamer(MCStreamer &S, const MCInst &Inst); 87 bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, 88 const MachineInstr *MI); 89 90 typedef std::tuple<unsigned, uint32_t> HwasanMemaccessTuple; 91 std::map<HwasanMemaccessTuple, MCSymbol *> HwasanMemaccessSymbols; 92 void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI); 93 void LowerKCFI_CHECK(const MachineInstr &MI); 94 void EmitHwasanMemaccessSymbols(Module &M); 95 96 // Wrapper needed for tblgenned pseudo lowering. 97 bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const; 98 99 void emitStartOfAsmFile(Module &M) override; 100 void emitEndOfAsmFile(Module &M) override; 101 102 void emitFunctionEntryLabel() override; 103 bool emitDirectiveOptionArch(); 104 105 private: 106 void emitAttributes(const MCSubtargetInfo &SubtargetInfo); 107 108 void emitNTLHint(const MachineInstr *MI); 109 110 bool lowerToMCInst(const MachineInstr *MI, MCInst &OutMI); 111 }; 112 } 113 114 void RISCVAsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, 115 const MachineInstr &MI) { 116 unsigned NOPBytes = STI->hasStdExtCOrZca() ? 2 : 4; 117 unsigned NumNOPBytes = StackMapOpers(&MI).getNumPatchBytes(); 118 119 auto &Ctx = OutStreamer.getContext(); 120 MCSymbol *MILabel = Ctx.createTempSymbol(); 121 OutStreamer.emitLabel(MILabel); 122 123 SM.recordStackMap(*MILabel, MI); 124 assert(NumNOPBytes % NOPBytes == 0 && 125 "Invalid number of NOP bytes requested!"); 126 127 // Scan ahead to trim the shadow. 128 const MachineBasicBlock &MBB = *MI.getParent(); 129 MachineBasicBlock::const_iterator MII(MI); 130 ++MII; 131 while (NumNOPBytes > 0) { 132 if (MII == MBB.end() || MII->isCall() || 133 MII->getOpcode() == RISCV::DBG_VALUE || 134 MII->getOpcode() == TargetOpcode::PATCHPOINT || 135 MII->getOpcode() == TargetOpcode::STACKMAP) 136 break; 137 ++MII; 138 NumNOPBytes -= 4; 139 } 140 141 // Emit nops. 142 emitNops(NumNOPBytes / NOPBytes); 143 } 144 145 // Lower a patchpoint of the form: 146 // [<def>], <id>, <numBytes>, <target>, <numArgs> 147 void RISCVAsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, 148 const MachineInstr &MI) { 149 unsigned NOPBytes = STI->hasStdExtCOrZca() ? 2 : 4; 150 151 auto &Ctx = OutStreamer.getContext(); 152 MCSymbol *MILabel = Ctx.createTempSymbol(); 153 OutStreamer.emitLabel(MILabel); 154 SM.recordPatchPoint(*MILabel, MI); 155 156 PatchPointOpers Opers(&MI); 157 158 const MachineOperand &CalleeMO = Opers.getCallTarget(); 159 unsigned EncodedBytes = 0; 160 161 if (CalleeMO.isImm()) { 162 uint64_t CallTarget = CalleeMO.getImm(); 163 if (CallTarget) { 164 assert((CallTarget & 0xFFFF'FFFF'FFFF) == CallTarget && 165 "High 16 bits of call target should be zero."); 166 // Materialize the jump address: 167 SmallVector<MCInst, 8> Seq; 168 RISCVMatInt::generateMCInstSeq(CallTarget, *STI, RISCV::X1, Seq); 169 for (MCInst &Inst : Seq) { 170 bool Compressed = EmitToStreamer(OutStreamer, Inst); 171 EncodedBytes += Compressed ? 2 : 4; 172 } 173 bool Compressed = EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR) 174 .addReg(RISCV::X1) 175 .addReg(RISCV::X1) 176 .addImm(0)); 177 EncodedBytes += Compressed ? 2 : 4; 178 } 179 } else if (CalleeMO.isGlobal()) { 180 MCOperand CallTargetMCOp; 181 lowerOperand(CalleeMO, CallTargetMCOp); 182 EmitToStreamer(OutStreamer, 183 MCInstBuilder(RISCV::PseudoCALL).addOperand(CallTargetMCOp)); 184 EncodedBytes += 8; 185 } 186 187 // Emit padding. 188 unsigned NumBytes = Opers.getNumPatchBytes(); 189 assert(NumBytes >= EncodedBytes && 190 "Patchpoint can't request size less than the length of a call."); 191 assert((NumBytes - EncodedBytes) % NOPBytes == 0 && 192 "Invalid number of NOP bytes requested!"); 193 emitNops((NumBytes - EncodedBytes) / NOPBytes); 194 } 195 196 void RISCVAsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM, 197 const MachineInstr &MI) { 198 unsigned NOPBytes = STI->hasStdExtCOrZca() ? 2 : 4; 199 200 StatepointOpers SOpers(&MI); 201 if (unsigned PatchBytes = SOpers.getNumPatchBytes()) { 202 assert(PatchBytes % NOPBytes == 0 && 203 "Invalid number of NOP bytes requested!"); 204 emitNops(PatchBytes / NOPBytes); 205 } else { 206 // Lower call target and choose correct opcode 207 const MachineOperand &CallTarget = SOpers.getCallTarget(); 208 MCOperand CallTargetMCOp; 209 switch (CallTarget.getType()) { 210 case MachineOperand::MO_GlobalAddress: 211 case MachineOperand::MO_ExternalSymbol: 212 lowerOperand(CallTarget, CallTargetMCOp); 213 EmitToStreamer( 214 OutStreamer, 215 MCInstBuilder(RISCV::PseudoCALL).addOperand(CallTargetMCOp)); 216 break; 217 case MachineOperand::MO_Immediate: 218 CallTargetMCOp = MCOperand::createImm(CallTarget.getImm()); 219 EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JAL) 220 .addReg(RISCV::X1) 221 .addOperand(CallTargetMCOp)); 222 break; 223 case MachineOperand::MO_Register: 224 CallTargetMCOp = MCOperand::createReg(CallTarget.getReg()); 225 EmitToStreamer(OutStreamer, MCInstBuilder(RISCV::JALR) 226 .addReg(RISCV::X1) 227 .addOperand(CallTargetMCOp) 228 .addImm(0)); 229 break; 230 default: 231 llvm_unreachable("Unsupported operand type in statepoint call target"); 232 break; 233 } 234 } 235 236 auto &Ctx = OutStreamer.getContext(); 237 MCSymbol *MILabel = Ctx.createTempSymbol(); 238 OutStreamer.emitLabel(MILabel); 239 SM.recordStatepoint(*MILabel, MI); 240 } 241 242 bool RISCVAsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) { 243 MCInst CInst; 244 bool Res = RISCVRVC::compress(CInst, Inst, *STI); 245 if (Res) 246 ++RISCVNumInstrsCompressed; 247 AsmPrinter::EmitToStreamer(*OutStreamer, Res ? CInst : Inst); 248 return Res; 249 } 250 251 // Simple pseudo-instructions have their lowering (with expansion to real 252 // instructions) auto-generated. 253 #include "RISCVGenMCPseudoLowering.inc" 254 255 // If the target supports Zihintntl and the instruction has a nontemporal 256 // MachineMemOperand, emit an NTLH hint instruction before it. 257 void RISCVAsmPrinter::emitNTLHint(const MachineInstr *MI) { 258 if (!STI->hasStdExtZihintntl()) 259 return; 260 261 if (MI->memoperands_empty()) 262 return; 263 264 MachineMemOperand *MMO = *(MI->memoperands_begin()); 265 if (!MMO->isNonTemporal()) 266 return; 267 268 unsigned NontemporalMode = 0; 269 if (MMO->getFlags() & MONontemporalBit0) 270 NontemporalMode += 0b1; 271 if (MMO->getFlags() & MONontemporalBit1) 272 NontemporalMode += 0b10; 273 274 MCInst Hint; 275 if (STI->hasStdExtCOrZca() && STI->enableRVCHintInstrs()) 276 Hint.setOpcode(RISCV::C_ADD_HINT); 277 else 278 Hint.setOpcode(RISCV::ADD); 279 280 Hint.addOperand(MCOperand::createReg(RISCV::X0)); 281 Hint.addOperand(MCOperand::createReg(RISCV::X0)); 282 Hint.addOperand(MCOperand::createReg(RISCV::X2 + NontemporalMode)); 283 284 EmitToStreamer(*OutStreamer, Hint); 285 } 286 287 void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) { 288 RISCV_MC::verifyInstructionPredicates(MI->getOpcode(), 289 getSubtargetInfo().getFeatureBits()); 290 291 emitNTLHint(MI); 292 293 // Do any auto-generated pseudo lowerings. 294 if (emitPseudoExpansionLowering(*OutStreamer, MI)) 295 return; 296 297 298 switch (MI->getOpcode()) { 299 case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES: 300 LowerHWASAN_CHECK_MEMACCESS(*MI); 301 return; 302 case RISCV::KCFI_CHECK: 303 LowerKCFI_CHECK(*MI); 304 return; 305 case RISCV::PseudoRVVInitUndefM1: 306 case RISCV::PseudoRVVInitUndefM2: 307 case RISCV::PseudoRVVInitUndefM4: 308 case RISCV::PseudoRVVInitUndefM8: 309 return; 310 case TargetOpcode::STACKMAP: 311 return LowerSTACKMAP(*OutStreamer, SM, *MI); 312 case TargetOpcode::PATCHPOINT: 313 return LowerPATCHPOINT(*OutStreamer, SM, *MI); 314 case TargetOpcode::STATEPOINT: 315 return LowerSTATEPOINT(*OutStreamer, SM, *MI); 316 } 317 318 MCInst OutInst; 319 if (!lowerToMCInst(MI, OutInst)) 320 EmitToStreamer(*OutStreamer, OutInst); 321 } 322 323 bool RISCVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, 324 const char *ExtraCode, raw_ostream &OS) { 325 // First try the generic code, which knows about modifiers like 'c' and 'n'. 326 if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS)) 327 return false; 328 329 const MachineOperand &MO = MI->getOperand(OpNo); 330 if (ExtraCode && ExtraCode[0]) { 331 if (ExtraCode[1] != 0) 332 return true; // Unknown modifier. 333 334 switch (ExtraCode[0]) { 335 default: 336 return true; // Unknown modifier. 337 case 'z': // Print zero register if zero, regular printing otherwise. 338 if (MO.isImm() && MO.getImm() == 0) { 339 OS << RISCVInstPrinter::getRegisterName(RISCV::X0); 340 return false; 341 } 342 break; 343 case 'i': // Literal 'i' if operand is not a register. 344 if (!MO.isReg()) 345 OS << 'i'; 346 return false; 347 } 348 } 349 350 switch (MO.getType()) { 351 case MachineOperand::MO_Immediate: 352 OS << MO.getImm(); 353 return false; 354 case MachineOperand::MO_Register: 355 OS << RISCVInstPrinter::getRegisterName(MO.getReg()); 356 return false; 357 case MachineOperand::MO_GlobalAddress: 358 PrintSymbolOperand(MO, OS); 359 return false; 360 case MachineOperand::MO_BlockAddress: { 361 MCSymbol *Sym = GetBlockAddressSymbol(MO.getBlockAddress()); 362 Sym->print(OS, MAI); 363 return false; 364 } 365 default: 366 break; 367 } 368 369 return true; 370 } 371 372 bool RISCVAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, 373 unsigned OpNo, 374 const char *ExtraCode, 375 raw_ostream &OS) { 376 if (ExtraCode) 377 return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); 378 379 const MachineOperand &AddrReg = MI->getOperand(OpNo); 380 assert(MI->getNumOperands() > OpNo + 1 && "Expected additional operand"); 381 const MachineOperand &Offset = MI->getOperand(OpNo + 1); 382 // All memory operands should have a register and an immediate operand (see 383 // RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand). 384 if (!AddrReg.isReg()) 385 return true; 386 if (!Offset.isImm() && !Offset.isGlobal() && !Offset.isBlockAddress() && 387 !Offset.isMCSymbol()) 388 return true; 389 390 MCOperand MCO; 391 if (!lowerOperand(Offset, MCO)) 392 return true; 393 394 if (Offset.isImm()) 395 OS << MCO.getImm(); 396 else if (Offset.isGlobal() || Offset.isBlockAddress() || Offset.isMCSymbol()) 397 OS << *MCO.getExpr(); 398 OS << "(" << RISCVInstPrinter::getRegisterName(AddrReg.getReg()) << ")"; 399 return false; 400 } 401 402 bool RISCVAsmPrinter::emitDirectiveOptionArch() { 403 RISCVTargetStreamer &RTS = 404 static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); 405 SmallVector<RISCVOptionArchArg> NeedEmitStdOptionArgs; 406 const MCSubtargetInfo &MCSTI = *TM.getMCSubtargetInfo(); 407 for (const auto &Feature : RISCVFeatureKV) { 408 if (STI->hasFeature(Feature.Value) == MCSTI.hasFeature(Feature.Value)) 409 continue; 410 411 if (!llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature.Key)) 412 continue; 413 414 auto Delta = STI->hasFeature(Feature.Value) ? RISCVOptionArchArgType::Plus 415 : RISCVOptionArchArgType::Minus; 416 NeedEmitStdOptionArgs.emplace_back(Delta, Feature.Key); 417 } 418 if (!NeedEmitStdOptionArgs.empty()) { 419 RTS.emitDirectiveOptionPush(); 420 RTS.emitDirectiveOptionArch(NeedEmitStdOptionArgs); 421 return true; 422 } 423 424 return false; 425 } 426 427 bool RISCVAsmPrinter::runOnMachineFunction(MachineFunction &MF) { 428 STI = &MF.getSubtarget<RISCVSubtarget>(); 429 RISCVTargetStreamer &RTS = 430 static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); 431 432 bool EmittedOptionArch = emitDirectiveOptionArch(); 433 434 SetupMachineFunction(MF); 435 emitFunctionBody(); 436 437 if (EmittedOptionArch) 438 RTS.emitDirectiveOptionPop(); 439 return false; 440 } 441 442 void RISCVAsmPrinter::emitStartOfAsmFile(Module &M) { 443 RISCVTargetStreamer &RTS = 444 static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); 445 if (const MDString *ModuleTargetABI = 446 dyn_cast_or_null<MDString>(M.getModuleFlag("target-abi"))) 447 RTS.setTargetABI(RISCVABI::getTargetABI(ModuleTargetABI->getString())); 448 449 MCSubtargetInfo SubtargetInfo = *TM.getMCSubtargetInfo(); 450 451 // Use module flag to update feature bits. 452 if (auto *MD = dyn_cast_or_null<MDNode>(M.getModuleFlag("riscv-isa"))) { 453 for (auto &ISA : MD->operands()) { 454 if (auto *ISAString = dyn_cast_or_null<MDString>(ISA)) { 455 auto ParseResult = llvm::RISCVISAInfo::parseArchString( 456 ISAString->getString(), /*EnableExperimentalExtension=*/true, 457 /*ExperimentalExtensionVersionCheck=*/true); 458 if (!errorToBool(ParseResult.takeError())) { 459 auto &ISAInfo = *ParseResult; 460 for (const auto &Feature : RISCVFeatureKV) { 461 if (ISAInfo->hasExtension(Feature.Key) && 462 !SubtargetInfo.hasFeature(Feature.Value)) 463 SubtargetInfo.ToggleFeature(Feature.Key); 464 } 465 } 466 } 467 } 468 469 RTS.setFlagsFromFeatures(SubtargetInfo); 470 } 471 472 if (TM.getTargetTriple().isOSBinFormatELF()) 473 emitAttributes(SubtargetInfo); 474 } 475 476 void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) { 477 RISCVTargetStreamer &RTS = 478 static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); 479 480 if (TM.getTargetTriple().isOSBinFormatELF()) 481 RTS.finishAttributeSection(); 482 EmitHwasanMemaccessSymbols(M); 483 } 484 485 void RISCVAsmPrinter::emitAttributes(const MCSubtargetInfo &SubtargetInfo) { 486 RISCVTargetStreamer &RTS = 487 static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); 488 // Use MCSubtargetInfo from TargetMachine. Individual functions may have 489 // attributes that differ from other functions in the module and we have no 490 // way to know which function is correct. 491 RTS.emitTargetAttributes(SubtargetInfo, /*EmitStackAlign*/ true); 492 } 493 494 void RISCVAsmPrinter::emitFunctionEntryLabel() { 495 const auto *RMFI = MF->getInfo<RISCVMachineFunctionInfo>(); 496 if (RMFI->isVectorCall()) { 497 auto &RTS = 498 static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); 499 RTS.emitDirectiveVariantCC(*CurrentFnSym); 500 } 501 return AsmPrinter::emitFunctionEntryLabel(); 502 } 503 504 // Force static initialization. 505 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVAsmPrinter() { 506 RegisterAsmPrinter<RISCVAsmPrinter> X(getTheRISCV32Target()); 507 RegisterAsmPrinter<RISCVAsmPrinter> Y(getTheRISCV64Target()); 508 } 509 510 void RISCVAsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI) { 511 Register Reg = MI.getOperand(0).getReg(); 512 uint32_t AccessInfo = MI.getOperand(1).getImm(); 513 MCSymbol *&Sym = 514 HwasanMemaccessSymbols[HwasanMemaccessTuple(Reg, AccessInfo)]; 515 if (!Sym) { 516 // FIXME: Make this work on non-ELF. 517 if (!TM.getTargetTriple().isOSBinFormatELF()) 518 report_fatal_error("llvm.hwasan.check.memaccess only supported on ELF"); 519 520 std::string SymName = "__hwasan_check_x" + utostr(Reg - RISCV::X0) + "_" + 521 utostr(AccessInfo) + "_short"; 522 Sym = OutContext.getOrCreateSymbol(SymName); 523 } 524 auto Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, OutContext); 525 auto Expr = RISCVMCExpr::create(Res, RISCVMCExpr::VK_RISCV_CALL, OutContext); 526 527 EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::PseudoCALL).addExpr(Expr)); 528 } 529 530 void RISCVAsmPrinter::LowerKCFI_CHECK(const MachineInstr &MI) { 531 Register AddrReg = MI.getOperand(0).getReg(); 532 assert(std::next(MI.getIterator())->isCall() && 533 "KCFI_CHECK not followed by a call instruction"); 534 assert(std::next(MI.getIterator())->getOperand(0).getReg() == AddrReg && 535 "KCFI_CHECK call target doesn't match call operand"); 536 537 // Temporary registers for comparing the hashes. If a register is used 538 // for the call target, or reserved by the user, we can clobber another 539 // temporary register as the check is immediately followed by the 540 // call. The check defaults to X6/X7, but can fall back to X28-X31 if 541 // needed. 542 unsigned ScratchRegs[] = {RISCV::X6, RISCV::X7}; 543 unsigned NextReg = RISCV::X28; 544 auto isRegAvailable = [&](unsigned Reg) { 545 return Reg != AddrReg && !STI->isRegisterReservedByUser(Reg); 546 }; 547 for (auto &Reg : ScratchRegs) { 548 if (isRegAvailable(Reg)) 549 continue; 550 while (!isRegAvailable(NextReg)) 551 ++NextReg; 552 Reg = NextReg++; 553 if (Reg > RISCV::X31) 554 report_fatal_error("Unable to find scratch registers for KCFI_CHECK"); 555 } 556 557 if (AddrReg == RISCV::X0) { 558 // Checking X0 makes no sense. Instead of emitting a load, zero 559 // ScratchRegs[0]. 560 EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::ADDI) 561 .addReg(ScratchRegs[0]) 562 .addReg(RISCV::X0) 563 .addImm(0)); 564 } else { 565 // Adjust the offset for patchable-function-prefix. This assumes that 566 // patchable-function-prefix is the same for all functions. 567 int NopSize = STI->hasStdExtCOrZca() ? 2 : 4; 568 int64_t PrefixNops = 0; 569 (void)MI.getMF() 570 ->getFunction() 571 .getFnAttribute("patchable-function-prefix") 572 .getValueAsString() 573 .getAsInteger(10, PrefixNops); 574 575 // Load the target function type hash. 576 EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::LW) 577 .addReg(ScratchRegs[0]) 578 .addReg(AddrReg) 579 .addImm(-(PrefixNops * NopSize + 4))); 580 } 581 582 // Load the expected 32-bit type hash. 583 const int64_t Type = MI.getOperand(1).getImm(); 584 const int64_t Hi20 = ((Type + 0x800) >> 12) & 0xFFFFF; 585 const int64_t Lo12 = SignExtend64<12>(Type); 586 if (Hi20) { 587 EmitToStreamer( 588 *OutStreamer, 589 MCInstBuilder(RISCV::LUI).addReg(ScratchRegs[1]).addImm(Hi20)); 590 } 591 if (Lo12 || Hi20 == 0) { 592 EmitToStreamer(*OutStreamer, 593 MCInstBuilder((STI->hasFeature(RISCV::Feature64Bit) && Hi20) 594 ? RISCV::ADDIW 595 : RISCV::ADDI) 596 .addReg(ScratchRegs[1]) 597 .addReg(ScratchRegs[1]) 598 .addImm(Lo12)); 599 } 600 601 // Compare the hashes and trap if there's a mismatch. 602 MCSymbol *Pass = OutContext.createTempSymbol(); 603 EmitToStreamer(*OutStreamer, 604 MCInstBuilder(RISCV::BEQ) 605 .addReg(ScratchRegs[0]) 606 .addReg(ScratchRegs[1]) 607 .addExpr(MCSymbolRefExpr::create(Pass, OutContext))); 608 609 MCSymbol *Trap = OutContext.createTempSymbol(); 610 OutStreamer->emitLabel(Trap); 611 EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::EBREAK)); 612 emitKCFITrapEntry(*MI.getMF(), Trap); 613 OutStreamer->emitLabel(Pass); 614 } 615 616 void RISCVAsmPrinter::EmitHwasanMemaccessSymbols(Module &M) { 617 if (HwasanMemaccessSymbols.empty()) 618 return; 619 620 assert(TM.getTargetTriple().isOSBinFormatELF()); 621 // Use MCSubtargetInfo from TargetMachine. Individual functions may have 622 // attributes that differ from other functions in the module and we have no 623 // way to know which function is correct. 624 const MCSubtargetInfo &MCSTI = *TM.getMCSubtargetInfo(); 625 626 MCSymbol *HwasanTagMismatchV2Sym = 627 OutContext.getOrCreateSymbol("__hwasan_tag_mismatch_v2"); 628 // Annotate symbol as one having incompatible calling convention, so 629 // run-time linkers can instead eagerly bind this function. 630 auto &RTS = 631 static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer()); 632 RTS.emitDirectiveVariantCC(*HwasanTagMismatchV2Sym); 633 634 const MCSymbolRefExpr *HwasanTagMismatchV2Ref = 635 MCSymbolRefExpr::create(HwasanTagMismatchV2Sym, OutContext); 636 auto Expr = RISCVMCExpr::create(HwasanTagMismatchV2Ref, 637 RISCVMCExpr::VK_RISCV_CALL, OutContext); 638 639 for (auto &P : HwasanMemaccessSymbols) { 640 unsigned Reg = std::get<0>(P.first); 641 uint32_t AccessInfo = std::get<1>(P.first); 642 MCSymbol *Sym = P.second; 643 644 unsigned Size = 645 1 << ((AccessInfo >> HWASanAccessInfo::AccessSizeShift) & 0xf); 646 OutStreamer->switchSection(OutContext.getELFSection( 647 ".text.hot", ELF::SHT_PROGBITS, 648 ELF::SHF_EXECINSTR | ELF::SHF_ALLOC | ELF::SHF_GROUP, 0, Sym->getName(), 649 /*IsComdat=*/true)); 650 651 OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction); 652 OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak); 653 OutStreamer->emitSymbolAttribute(Sym, MCSA_Hidden); 654 OutStreamer->emitLabel(Sym); 655 656 // Extract shadow offset from ptr 657 OutStreamer->emitInstruction( 658 MCInstBuilder(RISCV::SLLI).addReg(RISCV::X6).addReg(Reg).addImm(8), 659 MCSTI); 660 OutStreamer->emitInstruction(MCInstBuilder(RISCV::SRLI) 661 .addReg(RISCV::X6) 662 .addReg(RISCV::X6) 663 .addImm(12), 664 MCSTI); 665 // load shadow tag in X6, X5 contains shadow base 666 OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADD) 667 .addReg(RISCV::X6) 668 .addReg(RISCV::X5) 669 .addReg(RISCV::X6), 670 MCSTI); 671 OutStreamer->emitInstruction( 672 MCInstBuilder(RISCV::LBU).addReg(RISCV::X6).addReg(RISCV::X6).addImm(0), 673 MCSTI); 674 // Extract tag from X5 and compare it with loaded tag from shadow 675 OutStreamer->emitInstruction( 676 MCInstBuilder(RISCV::SRLI).addReg(RISCV::X7).addReg(Reg).addImm(56), 677 MCSTI); 678 MCSymbol *HandleMismatchOrPartialSym = OutContext.createTempSymbol(); 679 // X7 contains tag from memory, while X6 contains tag from the pointer 680 OutStreamer->emitInstruction( 681 MCInstBuilder(RISCV::BNE) 682 .addReg(RISCV::X7) 683 .addReg(RISCV::X6) 684 .addExpr(MCSymbolRefExpr::create(HandleMismatchOrPartialSym, 685 OutContext)), 686 MCSTI); 687 MCSymbol *ReturnSym = OutContext.createTempSymbol(); 688 OutStreamer->emitLabel(ReturnSym); 689 OutStreamer->emitInstruction(MCInstBuilder(RISCV::JALR) 690 .addReg(RISCV::X0) 691 .addReg(RISCV::X1) 692 .addImm(0), 693 MCSTI); 694 OutStreamer->emitLabel(HandleMismatchOrPartialSym); 695 696 OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) 697 .addReg(RISCV::X28) 698 .addReg(RISCV::X0) 699 .addImm(16), 700 MCSTI); 701 MCSymbol *HandleMismatchSym = OutContext.createTempSymbol(); 702 OutStreamer->emitInstruction( 703 MCInstBuilder(RISCV::BGEU) 704 .addReg(RISCV::X6) 705 .addReg(RISCV::X28) 706 .addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)), 707 MCSTI); 708 709 OutStreamer->emitInstruction( 710 MCInstBuilder(RISCV::ANDI).addReg(RISCV::X28).addReg(Reg).addImm(0xF), 711 MCSTI); 712 713 if (Size != 1) 714 OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) 715 .addReg(RISCV::X28) 716 .addReg(RISCV::X28) 717 .addImm(Size - 1), 718 MCSTI); 719 OutStreamer->emitInstruction( 720 MCInstBuilder(RISCV::BGE) 721 .addReg(RISCV::X28) 722 .addReg(RISCV::X6) 723 .addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)), 724 MCSTI); 725 726 OutStreamer->emitInstruction( 727 MCInstBuilder(RISCV::ORI).addReg(RISCV::X6).addReg(Reg).addImm(0xF), 728 MCSTI); 729 OutStreamer->emitInstruction( 730 MCInstBuilder(RISCV::LBU).addReg(RISCV::X6).addReg(RISCV::X6).addImm(0), 731 MCSTI); 732 OutStreamer->emitInstruction( 733 MCInstBuilder(RISCV::BEQ) 734 .addReg(RISCV::X6) 735 .addReg(RISCV::X7) 736 .addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)), 737 MCSTI); 738 739 OutStreamer->emitLabel(HandleMismatchSym); 740 741 // | Previous stack frames... | 742 // +=================================+ <-- [SP + 256] 743 // | ... | 744 // | | 745 // | Stack frame space for x12 - x31.| 746 // | | 747 // | ... | 748 // +---------------------------------+ <-- [SP + 96] 749 // | Saved x11(arg1), as | 750 // | __hwasan_check_* clobbers it. | 751 // +---------------------------------+ <-- [SP + 88] 752 // | Saved x10(arg0), as | 753 // | __hwasan_check_* clobbers it. | 754 // +---------------------------------+ <-- [SP + 80] 755 // | | 756 // | Stack frame space for x9. | 757 // +---------------------------------+ <-- [SP + 72] 758 // | | 759 // | Saved x8(fp), as | 760 // | __hwasan_check_* clobbers it. | 761 // +---------------------------------+ <-- [SP + 64] 762 // | ... | 763 // | | 764 // | Stack frame space for x2 - x7. | 765 // | | 766 // | ... | 767 // +---------------------------------+ <-- [SP + 16] 768 // | Return address (x1) for caller | 769 // | of __hwasan_check_*. | 770 // +---------------------------------+ <-- [SP + 8] 771 // | Reserved place for x0, possibly | 772 // | junk, since we don't save it. | 773 // +---------------------------------+ <-- [x2 / SP] 774 775 // Adjust sp 776 OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) 777 .addReg(RISCV::X2) 778 .addReg(RISCV::X2) 779 .addImm(-256), 780 MCSTI); 781 782 // store x10(arg0) by new sp 783 OutStreamer->emitInstruction(MCInstBuilder(RISCV::SD) 784 .addReg(RISCV::X10) 785 .addReg(RISCV::X2) 786 .addImm(8 * 10), 787 MCSTI); 788 // store x11(arg1) by new sp 789 OutStreamer->emitInstruction(MCInstBuilder(RISCV::SD) 790 .addReg(RISCV::X11) 791 .addReg(RISCV::X2) 792 .addImm(8 * 11), 793 MCSTI); 794 795 // store x8(fp) by new sp 796 OutStreamer->emitInstruction( 797 MCInstBuilder(RISCV::SD).addReg(RISCV::X8).addReg(RISCV::X2).addImm(8 * 798 8), 799 MCSTI); 800 // store x1(ra) by new sp 801 OutStreamer->emitInstruction( 802 MCInstBuilder(RISCV::SD).addReg(RISCV::X1).addReg(RISCV::X2).addImm(1 * 803 8), 804 MCSTI); 805 if (Reg != RISCV::X10) 806 OutStreamer->emitInstruction(MCInstBuilder(RISCV::ADDI) 807 .addReg(RISCV::X10) 808 .addReg(Reg) 809 .addImm(0), 810 MCSTI); 811 OutStreamer->emitInstruction( 812 MCInstBuilder(RISCV::ADDI) 813 .addReg(RISCV::X11) 814 .addReg(RISCV::X0) 815 .addImm(AccessInfo & HWASanAccessInfo::RuntimeMask), 816 MCSTI); 817 818 OutStreamer->emitInstruction(MCInstBuilder(RISCV::PseudoCALL).addExpr(Expr), 819 MCSTI); 820 } 821 } 822 823 static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym, 824 const AsmPrinter &AP) { 825 MCContext &Ctx = AP.OutContext; 826 RISCVMCExpr::VariantKind Kind; 827 828 switch (MO.getTargetFlags()) { 829 default: 830 llvm_unreachable("Unknown target flag on GV operand"); 831 case RISCVII::MO_None: 832 Kind = RISCVMCExpr::VK_RISCV_None; 833 break; 834 case RISCVII::MO_CALL: 835 Kind = RISCVMCExpr::VK_RISCV_CALL_PLT; 836 break; 837 case RISCVII::MO_LO: 838 Kind = RISCVMCExpr::VK_RISCV_LO; 839 break; 840 case RISCVII::MO_HI: 841 Kind = RISCVMCExpr::VK_RISCV_HI; 842 break; 843 case RISCVII::MO_PCREL_LO: 844 Kind = RISCVMCExpr::VK_RISCV_PCREL_LO; 845 break; 846 case RISCVII::MO_PCREL_HI: 847 Kind = RISCVMCExpr::VK_RISCV_PCREL_HI; 848 break; 849 case RISCVII::MO_GOT_HI: 850 Kind = RISCVMCExpr::VK_RISCV_GOT_HI; 851 break; 852 case RISCVII::MO_TPREL_LO: 853 Kind = RISCVMCExpr::VK_RISCV_TPREL_LO; 854 break; 855 case RISCVII::MO_TPREL_HI: 856 Kind = RISCVMCExpr::VK_RISCV_TPREL_HI; 857 break; 858 case RISCVII::MO_TPREL_ADD: 859 Kind = RISCVMCExpr::VK_RISCV_TPREL_ADD; 860 break; 861 case RISCVII::MO_TLS_GOT_HI: 862 Kind = RISCVMCExpr::VK_RISCV_TLS_GOT_HI; 863 break; 864 case RISCVII::MO_TLS_GD_HI: 865 Kind = RISCVMCExpr::VK_RISCV_TLS_GD_HI; 866 break; 867 case RISCVII::MO_TLSDESC_HI: 868 Kind = RISCVMCExpr::VK_RISCV_TLSDESC_HI; 869 break; 870 case RISCVII::MO_TLSDESC_LOAD_LO: 871 Kind = RISCVMCExpr::VK_RISCV_TLSDESC_LOAD_LO; 872 break; 873 case RISCVII::MO_TLSDESC_ADD_LO: 874 Kind = RISCVMCExpr::VK_RISCV_TLSDESC_ADD_LO; 875 break; 876 case RISCVII::MO_TLSDESC_CALL: 877 Kind = RISCVMCExpr::VK_RISCV_TLSDESC_CALL; 878 break; 879 } 880 881 const MCExpr *ME = 882 MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx); 883 884 if (!MO.isJTI() && !MO.isMBB() && MO.getOffset()) 885 ME = MCBinaryExpr::createAdd( 886 ME, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); 887 888 if (Kind != RISCVMCExpr::VK_RISCV_None) 889 ME = RISCVMCExpr::create(ME, Kind, Ctx); 890 return MCOperand::createExpr(ME); 891 } 892 893 bool RISCVAsmPrinter::lowerOperand(const MachineOperand &MO, 894 MCOperand &MCOp) const { 895 switch (MO.getType()) { 896 default: 897 report_fatal_error("lowerOperand: unknown operand type"); 898 case MachineOperand::MO_Register: 899 // Ignore all implicit register operands. 900 if (MO.isImplicit()) 901 return false; 902 MCOp = MCOperand::createReg(MO.getReg()); 903 break; 904 case MachineOperand::MO_RegisterMask: 905 // Regmasks are like implicit defs. 906 return false; 907 case MachineOperand::MO_Immediate: 908 MCOp = MCOperand::createImm(MO.getImm()); 909 break; 910 case MachineOperand::MO_MachineBasicBlock: 911 MCOp = lowerSymbolOperand(MO, MO.getMBB()->getSymbol(), *this); 912 break; 913 case MachineOperand::MO_GlobalAddress: 914 MCOp = lowerSymbolOperand(MO, getSymbolPreferLocal(*MO.getGlobal()), *this); 915 break; 916 case MachineOperand::MO_BlockAddress: 917 MCOp = lowerSymbolOperand(MO, GetBlockAddressSymbol(MO.getBlockAddress()), 918 *this); 919 break; 920 case MachineOperand::MO_ExternalSymbol: 921 MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO.getSymbolName()), 922 *this); 923 break; 924 case MachineOperand::MO_ConstantPoolIndex: 925 MCOp = lowerSymbolOperand(MO, GetCPISymbol(MO.getIndex()), *this); 926 break; 927 case MachineOperand::MO_JumpTableIndex: 928 MCOp = lowerSymbolOperand(MO, GetJTISymbol(MO.getIndex()), *this); 929 break; 930 case MachineOperand::MO_MCSymbol: 931 MCOp = lowerSymbolOperand(MO, MO.getMCSymbol(), *this); 932 break; 933 } 934 return true; 935 } 936 937 static bool lowerRISCVVMachineInstrToMCInst(const MachineInstr *MI, 938 MCInst &OutMI) { 939 const RISCVVPseudosTable::PseudoInfo *RVV = 940 RISCVVPseudosTable::getPseudoInfo(MI->getOpcode()); 941 if (!RVV) 942 return false; 943 944 OutMI.setOpcode(RVV->BaseInstr); 945 946 const MachineBasicBlock *MBB = MI->getParent(); 947 assert(MBB && "MI expected to be in a basic block"); 948 const MachineFunction *MF = MBB->getParent(); 949 assert(MF && "MBB expected to be in a machine function"); 950 951 const RISCVSubtarget &Subtarget = MF->getSubtarget<RISCVSubtarget>(); 952 const TargetInstrInfo *TII = Subtarget.getInstrInfo(); 953 const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); 954 assert(TRI && "TargetRegisterInfo expected"); 955 956 const MCInstrDesc &MCID = MI->getDesc(); 957 uint64_t TSFlags = MCID.TSFlags; 958 unsigned NumOps = MI->getNumExplicitOperands(); 959 960 // Skip policy, SEW, VL, VXRM/FRM operands which are the last operands if 961 // present. 962 if (RISCVII::hasVecPolicyOp(TSFlags)) 963 --NumOps; 964 if (RISCVII::hasSEWOp(TSFlags)) 965 --NumOps; 966 if (RISCVII::hasVLOp(TSFlags)) 967 --NumOps; 968 if (RISCVII::hasRoundModeOp(TSFlags)) 969 --NumOps; 970 971 bool hasVLOutput = RISCV::isFaultFirstLoad(*MI); 972 for (unsigned OpNo = 0; OpNo != NumOps; ++OpNo) { 973 const MachineOperand &MO = MI->getOperand(OpNo); 974 // Skip vl ouput. It should be the second output. 975 if (hasVLOutput && OpNo == 1) 976 continue; 977 978 // Skip merge op. It should be the first operand after the defs. 979 if (OpNo == MI->getNumExplicitDefs() && MO.isReg() && MO.isTied()) { 980 assert(MCID.getOperandConstraint(OpNo, MCOI::TIED_TO) == 0 && 981 "Expected tied to first def."); 982 const MCInstrDesc &OutMCID = TII->get(OutMI.getOpcode()); 983 // Skip if the next operand in OutMI is not supposed to be tied. Unless it 984 // is a _TIED instruction. 985 if (OutMCID.getOperandConstraint(OutMI.getNumOperands(), MCOI::TIED_TO) < 986 0 && 987 !RISCVII::isTiedPseudo(TSFlags)) 988 continue; 989 } 990 991 MCOperand MCOp; 992 switch (MO.getType()) { 993 default: 994 llvm_unreachable("Unknown operand type"); 995 case MachineOperand::MO_Register: { 996 Register Reg = MO.getReg(); 997 998 if (RISCV::VRM2RegClass.contains(Reg) || 999 RISCV::VRM4RegClass.contains(Reg) || 1000 RISCV::VRM8RegClass.contains(Reg)) { 1001 Reg = TRI->getSubReg(Reg, RISCV::sub_vrm1_0); 1002 assert(Reg && "Subregister does not exist"); 1003 } else if (RISCV::FPR16RegClass.contains(Reg)) { 1004 Reg = 1005 TRI->getMatchingSuperReg(Reg, RISCV::sub_16, &RISCV::FPR32RegClass); 1006 assert(Reg && "Subregister does not exist"); 1007 } else if (RISCV::FPR64RegClass.contains(Reg)) { 1008 Reg = TRI->getSubReg(Reg, RISCV::sub_32); 1009 assert(Reg && "Superregister does not exist"); 1010 } else if (RISCV::VRN2M1RegClass.contains(Reg) || 1011 RISCV::VRN2M2RegClass.contains(Reg) || 1012 RISCV::VRN2M4RegClass.contains(Reg) || 1013 RISCV::VRN3M1RegClass.contains(Reg) || 1014 RISCV::VRN3M2RegClass.contains(Reg) || 1015 RISCV::VRN4M1RegClass.contains(Reg) || 1016 RISCV::VRN4M2RegClass.contains(Reg) || 1017 RISCV::VRN5M1RegClass.contains(Reg) || 1018 RISCV::VRN6M1RegClass.contains(Reg) || 1019 RISCV::VRN7M1RegClass.contains(Reg) || 1020 RISCV::VRN8M1RegClass.contains(Reg)) { 1021 Reg = TRI->getSubReg(Reg, RISCV::sub_vrm1_0); 1022 assert(Reg && "Subregister does not exist"); 1023 } 1024 1025 MCOp = MCOperand::createReg(Reg); 1026 break; 1027 } 1028 case MachineOperand::MO_Immediate: 1029 MCOp = MCOperand::createImm(MO.getImm()); 1030 break; 1031 } 1032 OutMI.addOperand(MCOp); 1033 } 1034 1035 // Unmasked pseudo instructions need to append dummy mask operand to 1036 // V instructions. All V instructions are modeled as the masked version. 1037 const MCInstrDesc &OutMCID = TII->get(OutMI.getOpcode()); 1038 if (OutMI.getNumOperands() < OutMCID.getNumOperands()) { 1039 assert(OutMCID.operands()[OutMI.getNumOperands()].RegClass == 1040 RISCV::VMV0RegClassID && 1041 "Expected only mask operand to be missing"); 1042 OutMI.addOperand(MCOperand::createReg(RISCV::NoRegister)); 1043 } 1044 1045 assert(OutMI.getNumOperands() == OutMCID.getNumOperands()); 1046 return true; 1047 } 1048 1049 bool RISCVAsmPrinter::lowerToMCInst(const MachineInstr *MI, MCInst &OutMI) { 1050 if (lowerRISCVVMachineInstrToMCInst(MI, OutMI)) 1051 return false; 1052 1053 OutMI.setOpcode(MI->getOpcode()); 1054 1055 for (const MachineOperand &MO : MI->operands()) { 1056 MCOperand MCOp; 1057 if (lowerOperand(MO, MCOp)) 1058 OutMI.addOperand(MCOp); 1059 } 1060 1061 switch (OutMI.getOpcode()) { 1062 case TargetOpcode::PATCHABLE_FUNCTION_ENTER: { 1063 const Function &F = MI->getParent()->getParent()->getFunction(); 1064 if (F.hasFnAttribute("patchable-function-entry")) { 1065 unsigned Num; 1066 if (F.getFnAttribute("patchable-function-entry") 1067 .getValueAsString() 1068 .getAsInteger(10, Num)) 1069 return false; 1070 emitNops(Num); 1071 return true; 1072 } 1073 break; 1074 } 1075 } 1076 return false; 1077 } 1078