1 //===- subzero/src/IceTargetLoweringX8632.cpp - x86-32 lowering -----------===// 2 // 3 // The Subzero Code Generator 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// \brief Implements the TargetLoweringX8632 class, which consists almost 12 /// entirely of the lowering sequence for each high-level instruction. 13 /// 14 //===----------------------------------------------------------------------===// 15 16 #include "IceTargetLoweringX8632.h" 17 18 #include "IceTargetLoweringX8632Traits.h" 19 20 namespace X8632 { 21 std::unique_ptr<::Ice::TargetLowering> createTargetLowering(::Ice::Cfg *Func) { 22 return ::Ice::X8632::TargetX8632::create(Func); 23 } 24 25 std::unique_ptr<::Ice::TargetDataLowering> 26 createTargetDataLowering(::Ice::GlobalContext *Ctx) { 27 return ::Ice::X8632::TargetDataX86<::Ice::X8632::TargetX8632Traits>::create( 28 Ctx); 29 } 30 31 std::unique_ptr<::Ice::TargetHeaderLowering> 32 createTargetHeaderLowering(::Ice::GlobalContext *Ctx) { 33 return ::Ice::X8632::TargetHeaderX86::create(Ctx); 34 } 35 36 void staticInit(::Ice::GlobalContext *Ctx) { 37 ::Ice::X8632::TargetX8632::staticInit(Ctx); 38 if (Ice::getFlags().getUseNonsfi()) { 39 // In nonsfi, we need to reference the _GLOBAL_OFFSET_TABLE_ for accessing 40 // globals. The GOT is an external symbol (i.e., it is not defined in the 41 // pexe) so we need to register it as such so that ELF emission won't barf 42 // on an "unknown" symbol. The GOT is added to the External symbols list 43 // here because staticInit() is invoked in a single-thread context. 44 Ctx->getConstantExternSym(Ctx->getGlobalString(::Ice::GlobalOffsetTable)); 45 } 46 } 47 48 bool shouldBePooled(const class ::Ice::Constant *C) { 49 return ::Ice::X8632::TargetX8632::shouldBePooled(C); 50 } 51 52 ::Ice::Type getPointerType() { 53 return ::Ice::X8632::TargetX8632::getPointerType(); 54 } 55 56 } // end of namespace X8632 57 58 namespace Ice { 59 namespace X8632 { 60 61 //------------------------------------------------------------------------------ 62 // ______ ______ ______ __ ______ ______ 63 // /\__ _\ /\ == \ /\ __ \ /\ \ /\__ _\ /\ ___\ 64 // \/_/\ \/ \ \ __< \ \ __ \ \ \ \ \/_/\ \/ \ \___ \ 65 // \ \_\ \ \_\ \_\ \ \_\ \_\ \ \_\ \ \_\ \/\_____\ 66 // \/_/ \/_/ /_/ \/_/\/_/ \/_/ \/_/ \/_____/ 67 // 68 //------------------------------------------------------------------------------ 69 const TargetX8632Traits::TableFcmpType TargetX8632Traits::TableFcmp[] = { 70 #define X(val, dflt, swapS, C1, C2, swapV, pred) \ 71 { \ 72 dflt, swapS, X8632::Traits::Cond::C1, X8632::Traits::Cond::C2, swapV, \ 73 X8632::Traits::Cond::pred \ 74 } \ 75 , 76 FCMPX8632_TABLE 77 #undef X 78 }; 79 80 const size_t TargetX8632Traits::TableFcmpSize = llvm::array_lengthof(TableFcmp); 81 82 const TargetX8632Traits::TableIcmp32Type TargetX8632Traits::TableIcmp32[] = { 83 #define X(val, C_32, C1_64, C2_64, C3_64) \ 84 { X8632::Traits::Cond::C_32 } \ 85 , 86 ICMPX8632_TABLE 87 #undef X 88 }; 89 90 const size_t TargetX8632Traits::TableIcmp32Size = 91 llvm::array_lengthof(TableIcmp32); 92 93 const TargetX8632Traits::TableIcmp64Type TargetX8632Traits::TableIcmp64[] = { 94 #define X(val, C_32, C1_64, C2_64, C3_64) \ 95 { \ 96 X8632::Traits::Cond::C1_64, X8632::Traits::Cond::C2_64, \ 97 X8632::Traits::Cond::C3_64 \ 98 } \ 99 , 100 ICMPX8632_TABLE 101 #undef X 102 }; 103 104 const size_t TargetX8632Traits::TableIcmp64Size = 105 llvm::array_lengthof(TableIcmp64); 106 107 const TargetX8632Traits::TableTypeX8632AttributesType 108 TargetX8632Traits::TableTypeX8632Attributes[] = { 109 #define X(tag, elty, cvt, sdss, pdps, spsd, int_, unpack, pack, width, fld) \ 110 { IceType_##elty } \ 111 , 112 ICETYPEX8632_TABLE 113 #undef X 114 }; 115 116 const size_t TargetX8632Traits::TableTypeX8632AttributesSize = 117 llvm::array_lengthof(TableTypeX8632Attributes); 118 119 #if defined(SUBZERO_USE_MICROSOFT_ABI) 120 // Windows 32-bit only guarantees 4 byte stack alignment 121 const uint32_t TargetX8632Traits::X86_STACK_ALIGNMENT_BYTES = 4; 122 #else 123 const uint32_t TargetX8632Traits::X86_STACK_ALIGNMENT_BYTES = 16; 124 #endif 125 const char *TargetX8632Traits::TargetName = "X8632"; 126 127 template <> 128 std::array<SmallBitVector, RCX86_NUM> 129 TargetX86Base<X8632::Traits>::TypeToRegisterSet = {{}}; 130 131 template <> 132 std::array<SmallBitVector, RCX86_NUM> 133 TargetX86Base<X8632::Traits>::TypeToRegisterSetUnfiltered = {{}}; 134 135 template <> 136 std::array<SmallBitVector, 137 TargetX86Base<X8632::Traits>::Traits::RegisterSet::Reg_NUM> 138 TargetX86Base<X8632::Traits>::RegisterAliases = {{}}; 139 140 template <> 141 FixupKind TargetX86Base<X8632::Traits>::PcRelFixup = 142 TargetX86Base<X8632::Traits>::Traits::FK_PcRel; 143 144 template <> 145 FixupKind TargetX86Base<X8632::Traits>::AbsFixup = 146 TargetX86Base<X8632::Traits>::Traits::FK_Abs; 147 148 //------------------------------------------------------------------------------ 149 // __ ______ __ __ ______ ______ __ __ __ ______ 150 // /\ \ /\ __ \/\ \ _ \ \/\ ___\/\ == \/\ \/\ "-.\ \/\ ___\ 151 // \ \ \___\ \ \/\ \ \ \/ ".\ \ \ __\\ \ __<\ \ \ \ \-. \ \ \__ \ 152 // \ \_____\ \_____\ \__/".~\_\ \_____\ \_\ \_\ \_\ \_\\"\_\ \_____\ 153 // \/_____/\/_____/\/_/ \/_/\/_____/\/_/ /_/\/_/\/_/ \/_/\/_____/ 154 // 155 //------------------------------------------------------------------------------ 156 void TargetX8632::_add_sp(Operand *Adjustment) { 157 Variable *esp = getPhysicalRegister(Traits::RegisterSet::Reg_esp); 158 _add(esp, Adjustment); 159 } 160 161 void TargetX8632::_mov_sp(Operand *NewValue) { 162 Variable *esp = getPhysicalRegister(Traits::RegisterSet::Reg_esp); 163 _redefined(_mov(esp, NewValue)); 164 } 165 166 Traits::X86OperandMem *TargetX8632::_sandbox_mem_reference(X86OperandMem *Mem) { 167 switch (SandboxingType) { 168 case ST_None: 169 case ST_NaCl: 170 return Mem; 171 case ST_Nonsfi: { 172 if (Mem->getIsRebased()) { 173 return Mem; 174 } 175 // For Non-SFI mode, if the Offset field is a ConstantRelocatable, we 176 // replace either Base or Index with a legalized RebasePtr. At emission 177 // time, the ConstantRelocatable will be emitted with the @GOTOFF 178 // relocation. 179 if (llvm::dyn_cast_or_null<ConstantRelocatable>(Mem->getOffset()) == 180 nullptr) { 181 return Mem; 182 } 183 Variable *T; 184 uint16_t Shift = 0; 185 if (Mem->getIndex() == nullptr) { 186 T = Mem->getBase(); 187 } else if (Mem->getBase() == nullptr) { 188 T = Mem->getIndex(); 189 Shift = Mem->getShift(); 190 } else { 191 llvm::report_fatal_error( 192 "Either Base or Index must be unused in Non-SFI mode"); 193 } 194 Variable *RebasePtrR = legalizeToReg(RebasePtr); 195 static constexpr bool IsRebased = true; 196 return Traits::X86OperandMem::create( 197 Func, Mem->getType(), RebasePtrR, Mem->getOffset(), T, Shift, 198 Traits::X86OperandMem::DefaultSegment, IsRebased); 199 } 200 } 201 llvm::report_fatal_error("Unhandled sandboxing type: " + 202 std::to_string(SandboxingType)); 203 } 204 205 void TargetX8632::_sub_sp(Operand *Adjustment) { 206 Variable *esp = getPhysicalRegister(Traits::RegisterSet::Reg_esp); 207 _sub(esp, Adjustment); 208 // Add a fake use of the stack pointer, to prevent the stack pointer adustment 209 // from being dead-code eliminated in a function that doesn't return. 210 Context.insert<InstFakeUse>(esp); 211 } 212 213 void TargetX8632::_link_bp() { 214 Variable *ebp = getPhysicalRegister(Traits::RegisterSet::Reg_ebp); 215 Variable *esp = getPhysicalRegister(Traits::RegisterSet::Reg_esp); 216 _push(ebp); 217 _mov(ebp, esp); 218 // Keep ebp live for late-stage liveness analysis (e.g. asm-verbose mode). 219 Context.insert<InstFakeUse>(ebp); 220 } 221 222 void TargetX8632::_unlink_bp() { 223 Variable *esp = getPhysicalRegister(Traits::RegisterSet::Reg_esp); 224 Variable *ebp = getPhysicalRegister(Traits::RegisterSet::Reg_ebp); 225 // For late-stage liveness analysis (e.g. asm-verbose mode), adding a fake 226 // use of esp before the assignment of esp=ebp keeps previous esp 227 // adjustments from being dead-code eliminated. 228 Context.insert<InstFakeUse>(esp); 229 _mov(esp, ebp); 230 _pop(ebp); 231 } 232 233 void TargetX8632::_push_reg(Variable *Reg) { _push(Reg); } 234 235 void TargetX8632::emitGetIP(CfgNode *Node) { 236 // If there is a non-deleted InstX86GetIP instruction, we need to move it to 237 // the point after the stack frame has stabilized but before 238 // register-allocated in-args are copied into their home registers. It would 239 // be slightly faster to search for the GetIP instruction before other prolog 240 // instructions are inserted, but it's more clear to do the whole 241 // transformation in a single place. 242 Traits::Insts::GetIP *GetIPInst = nullptr; 243 if (getFlags().getUseNonsfi()) { 244 for (Inst &Instr : Node->getInsts()) { 245 if (auto *GetIP = llvm::dyn_cast<Traits::Insts::GetIP>(&Instr)) { 246 if (!Instr.isDeleted()) 247 GetIPInst = GetIP; 248 break; 249 } 250 } 251 } 252 // Delete any existing InstX86GetIP instruction and reinsert it here. Also, 253 // insert the call to the helper function and the spill to the stack, to 254 // simplify emission. 255 if (GetIPInst) { 256 GetIPInst->setDeleted(); 257 Variable *Dest = GetIPInst->getDest(); 258 Variable *CallDest = 259 Dest->hasReg() ? Dest 260 : getPhysicalRegister(Traits::RegisterSet::Reg_eax); 261 auto *BeforeAddReloc = RelocOffset::create(Ctx); 262 BeforeAddReloc->setSubtract(true); 263 auto *BeforeAdd = InstX86Label::create(Func, this); 264 BeforeAdd->setRelocOffset(BeforeAddReloc); 265 266 auto *AfterAddReloc = RelocOffset::create(Ctx); 267 auto *AfterAdd = InstX86Label::create(Func, this); 268 AfterAdd->setRelocOffset(AfterAddReloc); 269 270 const RelocOffsetT ImmSize = -typeWidthInBytes(IceType_i32); 271 272 auto *GotFromPc = 273 llvm::cast<ConstantRelocatable>(Ctx->getConstantSymWithEmitString( 274 ImmSize, {AfterAddReloc, BeforeAddReloc}, 275 Ctx->getGlobalString(GlobalOffsetTable), GlobalOffsetTable)); 276 277 // Insert a new version of InstX86GetIP. 278 Context.insert<Traits::Insts::GetIP>(CallDest); 279 280 Context.insert(BeforeAdd); 281 _add(CallDest, GotFromPc); 282 Context.insert(AfterAdd); 283 284 // Spill the register to its home stack location if necessary. 285 if (Dest != CallDest) { 286 _mov(Dest, CallDest); 287 } 288 } 289 } 290 291 void TargetX8632::lowerIndirectJump(Variable *JumpTarget) { 292 AutoBundle _(this); 293 294 if (NeedSandboxing) { 295 const SizeT BundleSize = 296 1 << Func->getAssembler<>()->getBundleAlignLog2Bytes(); 297 _and(JumpTarget, Ctx->getConstantInt32(~(BundleSize - 1))); 298 } 299 300 _jmp(JumpTarget); 301 } 302 303 void TargetX8632::initRebasePtr() { 304 if (SandboxingType == ST_Nonsfi) { 305 RebasePtr = Func->makeVariable(IceType_i32); 306 } 307 } 308 309 void TargetX8632::initSandbox() { 310 if (SandboxingType != ST_Nonsfi) { 311 return; 312 } 313 // Insert the RebasePtr assignment as the very first lowered instruction. 314 // Later, it will be moved into the right place - after the stack frame is set 315 // up but before in-args are copied into registers. 316 Context.init(Func->getEntryNode()); 317 Context.setInsertPoint(Context.getCur()); 318 Context.insert<Traits::Insts::GetIP>(RebasePtr); 319 } 320 321 bool TargetX8632::legalizeOptAddrForSandbox(OptAddr *Addr) { 322 if (Addr->Relocatable == nullptr || SandboxingType != ST_Nonsfi) { 323 return true; 324 } 325 326 if (Addr->Base == RebasePtr || Addr->Index == RebasePtr) { 327 return true; 328 } 329 330 if (Addr->Base == nullptr) { 331 Addr->Base = RebasePtr; 332 return true; 333 } 334 335 if (Addr->Index == nullptr) { 336 Addr->Index = RebasePtr; 337 Addr->Shift = 0; 338 return true; 339 } 340 341 return false; 342 } 343 344 Inst *TargetX8632::emitCallToTarget(Operand *CallTarget, Variable *ReturnReg) { 345 std::unique_ptr<AutoBundle> Bundle; 346 if (NeedSandboxing) { 347 if (llvm::isa<Constant>(CallTarget)) { 348 Bundle = makeUnique<AutoBundle>(this, InstBundleLock::Opt_AlignToEnd); 349 } else { 350 Variable *CallTargetVar = nullptr; 351 _mov(CallTargetVar, CallTarget); 352 Bundle = makeUnique<AutoBundle>(this, InstBundleLock::Opt_AlignToEnd); 353 const SizeT BundleSize = 354 1 << Func->getAssembler<>()->getBundleAlignLog2Bytes(); 355 _and(CallTargetVar, Ctx->getConstantInt32(~(BundleSize - 1))); 356 CallTarget = CallTargetVar; 357 } 358 } 359 return Context.insert<Traits::Insts::Call>(ReturnReg, CallTarget); 360 } 361 362 Variable *TargetX8632::moveReturnValueToRegister(Operand *Value, 363 Type ReturnType) { 364 if (isVectorType(ReturnType)) { 365 return legalizeToReg(Value, Traits::RegisterSet::Reg_xmm0); 366 } else if (isScalarFloatingType(ReturnType)) { 367 _fld(Value); 368 return nullptr; 369 } else { 370 assert(ReturnType == IceType_i32 || ReturnType == IceType_i64); 371 if (ReturnType == IceType_i64) { 372 Variable *eax = 373 legalizeToReg(loOperand(Value), Traits::RegisterSet::Reg_eax); 374 Variable *edx = 375 legalizeToReg(hiOperand(Value), Traits::RegisterSet::Reg_edx); 376 Context.insert<InstFakeUse>(edx); 377 return eax; 378 } else { 379 Variable *Reg = nullptr; 380 _mov(Reg, Value, Traits::RegisterSet::Reg_eax); 381 return Reg; 382 } 383 } 384 } 385 386 void TargetX8632::emitSandboxedReturn() { 387 // Change the original ret instruction into a sandboxed return sequence. 388 // t:ecx = pop 389 // bundle_lock 390 // and t, ~31 391 // jmp *t 392 // bundle_unlock 393 // FakeUse <original_ret_operand> 394 Variable *T_ecx = makeReg(IceType_i32, Traits::RegisterSet::Reg_ecx); 395 _pop(T_ecx); 396 lowerIndirectJump(T_ecx); 397 } 398 399 // In some cases, there are x-macros tables for both high-level and low-level 400 // instructions/operands that use the same enum key value. The tables are kept 401 // separate to maintain a proper separation between abstraction layers. There 402 // is a risk that the tables could get out of sync if enum values are reordered 403 // or if entries are added or deleted. The following dummy namespaces use 404 // static_asserts to ensure everything is kept in sync. 405 406 namespace { 407 // Validate the enum values in FCMPX8632_TABLE. 408 namespace dummy1 { 409 // Define a temporary set of enum values based on low-level table entries. 410 enum _tmp_enum { 411 #define X(val, dflt, swapS, C1, C2, swapV, pred) _tmp_##val, 412 FCMPX8632_TABLE 413 #undef X 414 _num 415 }; 416 // Define a set of constants based on high-level table entries. 417 #define X(tag, str) static const int _table1_##tag = InstFcmp::tag; 418 ICEINSTFCMP_TABLE 419 #undef X 420 // Define a set of constants based on low-level table entries, and ensure the 421 // table entry keys are consistent. 422 #define X(val, dflt, swapS, C1, C2, swapV, pred) \ 423 static const int _table2_##val = _tmp_##val; \ 424 static_assert( \ 425 _table1_##val == _table2_##val, \ 426 "Inconsistency between FCMPX8632_TABLE and ICEINSTFCMP_TABLE"); 427 FCMPX8632_TABLE 428 #undef X 429 // Repeat the static asserts with respect to the high-level table entries in 430 // case the high-level table has extra entries. 431 #define X(tag, str) \ 432 static_assert( \ 433 _table1_##tag == _table2_##tag, \ 434 "Inconsistency between FCMPX8632_TABLE and ICEINSTFCMP_TABLE"); 435 ICEINSTFCMP_TABLE 436 #undef X 437 } // end of namespace dummy1 438 439 // Validate the enum values in ICMPX8632_TABLE. 440 namespace dummy2 { 441 // Define a temporary set of enum values based on low-level table entries. 442 enum _tmp_enum { 443 #define X(val, C_32, C1_64, C2_64, C3_64) _tmp_##val, 444 ICMPX8632_TABLE 445 #undef X 446 _num 447 }; 448 // Define a set of constants based on high-level table entries. 449 #define X(tag, reverse, str) static const int _table1_##tag = InstIcmp::tag; 450 ICEINSTICMP_TABLE 451 #undef X 452 // Define a set of constants based on low-level table entries, and ensure the 453 // table entry keys are consistent. 454 #define X(val, C_32, C1_64, C2_64, C3_64) \ 455 static const int _table2_##val = _tmp_##val; \ 456 static_assert( \ 457 _table1_##val == _table2_##val, \ 458 "Inconsistency between ICMPX8632_TABLE and ICEINSTICMP_TABLE"); 459 ICMPX8632_TABLE 460 #undef X 461 // Repeat the static asserts with respect to the high-level table entries in 462 // case the high-level table has extra entries. 463 #define X(tag, reverse, str) \ 464 static_assert( \ 465 _table1_##tag == _table2_##tag, \ 466 "Inconsistency between ICMPX8632_TABLE and ICEINSTICMP_TABLE"); 467 ICEINSTICMP_TABLE 468 #undef X 469 } // end of namespace dummy2 470 471 // Validate the enum values in ICETYPEX8632_TABLE. 472 namespace dummy3 { 473 // Define a temporary set of enum values based on low-level table entries. 474 enum _tmp_enum { 475 #define X(tag, elty, cvt, sdss, pdps, spsd, int_, unpack, pack, width, fld) \ 476 _tmp_##tag, 477 ICETYPEX8632_TABLE 478 #undef X 479 _num 480 }; 481 // Define a set of constants based on high-level table entries. 482 #define X(tag, sizeLog2, align, elts, elty, str, rcstr) \ 483 static const int _table1_##tag = IceType_##tag; 484 ICETYPE_TABLE 485 #undef X 486 // Define a set of constants based on low-level table entries, and ensure the 487 // table entry keys are consistent. 488 #define X(tag, elty, cvt, sdss, pdps, spsd, int_, unpack, pack, width, fld) \ 489 static const int _table2_##tag = _tmp_##tag; \ 490 static_assert(_table1_##tag == _table2_##tag, \ 491 "Inconsistency between ICETYPEX8632_TABLE and ICETYPE_TABLE"); 492 ICETYPEX8632_TABLE 493 #undef X 494 // Repeat the static asserts with respect to the high-level table entries in 495 // case the high-level table has extra entries. 496 #define X(tag, sizeLog2, align, elts, elty, str, rcstr) \ 497 static_assert(_table1_##tag == _table2_##tag, \ 498 "Inconsistency between ICETYPEX8632_TABLE and ICETYPE_TABLE"); 499 ICETYPE_TABLE 500 #undef X 501 } // end of namespace dummy3 502 } // end of anonymous namespace 503 504 } // end of namespace X8632 505 } // end of namespace Ice 506