Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
35294 views
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
1079