Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 //===- subzero/src/WasmTranslator.cpp - WASM to Subzero Translation -------===// | 1 //===- subzero/src/WasmTranslator.cpp - WASM to Subzero Translation -------===// |
| 2 // | 2 // |
| 3 // The Subzero Code Generator | 3 // The Subzero Code Generator |
| 4 // | 4 // |
| 5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
| 7 // | 7 // |
| 8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
| 9 /// | 9 /// |
| 10 /// \file | 10 /// \file |
| (...skipping 1047 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1058 LOG(out << "Call Result = " << Node(Dest) << "\n"); | 1058 LOG(out << "Call Result = " << Node(Dest) << "\n"); |
| 1059 return OperandNode(Dest); | 1059 return OperandNode(Dest); |
| 1060 } | 1060 } |
| 1061 Node CallIndirect(uint32_t SigIndex, Node *Args) { | 1061 Node CallIndirect(uint32_t SigIndex, Node *Args) { |
| 1062 LOG(out << "CallIndirect(" << SigIndex << ")\n"); | 1062 LOG(out << "CallIndirect(" << SigIndex << ")\n"); |
| 1063 // TODO(eholk): Compile to something better than a switch. | 1063 // TODO(eholk): Compile to something better than a switch. |
| 1064 const auto *Module = this->Module->module; | 1064 const auto *Module = this->Module->module; |
| 1065 assert(Module); | 1065 assert(Module); |
| 1066 const auto &IndirectTable = Module->function_table; | 1066 const auto &IndirectTable = Module->function_table; |
| 1067 | 1067 |
| 1068 // TODO(eholk): This should probably actually call abort instead. | 1068 auto *Abort = getAbortTarget(); |
| 1069 auto *Abort = Func->makeNode(); | |
| 1070 Abort->appendInst(InstUnreachable::create(Func)); | |
| 1071 | 1069 |
| 1072 assert(Args[0].toOperand()); | 1070 assert(Args[0].toOperand()); |
| 1073 | 1071 |
| 1074 auto *Switch = InstSwitch::create(Func, IndirectTable.size(), | 1072 auto *Switch = InstSwitch::create(Func, IndirectTable.size(), |
| 1075 Args[0].toOperand(), Abort); | 1073 Args[0].toOperand(), Abort); |
| 1076 assert(Abort); | 1074 assert(Abort); |
| 1077 | 1075 |
| 1078 const bool HasReturn = Module->signatures[SigIndex]->return_count() != 0; | 1076 const bool HasReturn = Module->signatures[SigIndex]->return_count() != 0; |
| 1079 const Ice::Type DestTy = | 1077 const Ice::Type DestTy = |
| 1080 HasReturn ? toIceType(Module->signatures[SigIndex]->GetReturn()) | 1078 HasReturn ? toIceType(Module->signatures[SigIndex]->GetReturn()) |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1130 llvm::report_fatal_error("LoadGlobal"); | 1128 llvm::report_fatal_error("LoadGlobal"); |
| 1131 } | 1129 } |
| 1132 Node StoreGlobal(uint32_t Index, Node Val) { | 1130 Node StoreGlobal(uint32_t Index, Node Val) { |
| 1133 (void)Index; | 1131 (void)Index; |
| 1134 (void)Val; | 1132 (void)Val; |
| 1135 llvm::report_fatal_error("StoreGlobal"); | 1133 llvm::report_fatal_error("StoreGlobal"); |
| 1136 } | 1134 } |
| 1137 | 1135 |
| 1138 Operand *sanitizeAddress(Operand *Base, uint32_t Offset) { | 1136 Operand *sanitizeAddress(Operand *Base, uint32_t Offset) { |
| 1139 SizeT MemSize = Module->module->min_mem_pages * WASM_PAGE_SIZE; | 1137 SizeT MemSize = Module->module->min_mem_pages * WASM_PAGE_SIZE; |
| 1140 SizeT MemMask = MemSize - 1; | |
| 1141 | 1138 |
| 1142 bool ConstZeroBase = false; | 1139 bool ConstZeroBase = false; |
| 1143 | 1140 |
| 1144 // first, add the index and the offset together. | 1141 // first, add the index and the offset together. |
| 1145 if (auto *ConstBase = llvm::dyn_cast<ConstantInteger32>(Base)) { | 1142 if (auto *ConstBase = llvm::dyn_cast<ConstantInteger32>(Base)) { |
| 1146 uint32_t RealOffset = Offset + ConstBase->getValue(); | 1143 uint32_t RealOffset = Offset + ConstBase->getValue(); |
| 1147 RealOffset &= MemMask; | 1144 if (RealOffset >= MemSize) { |
| 1145 // We've proven this will always be an out of bounds access, so insert | |
| 1146 // an unconditional trap. | |
| 1147 Control()->appendInst(InstUnreachable::create(Func)); | |
| 1148 // It doesn't matter what we return here, so return something that will | |
| 1149 // allow the rest of code generation to happen. | |
| 1150 // | |
| 1151 // We might be tempted to just abort translation here, but out of bounds | |
| 1152 // memory access is a runtime trap, not a compile error. | |
| 1153 return Ctx->getConstantZero(IceType_i32); | |
|
Jim Stichnoth
2016/04/23 16:23:04
Maybe use getPointerType() instead of IceType_i32,
Eric Holk
2016/04/25 19:53:03
Done.
| |
| 1154 } | |
| 1148 Base = Ctx->getConstantInt32(RealOffset); | 1155 Base = Ctx->getConstantInt32(RealOffset); |
| 1149 ConstZeroBase = (0 == RealOffset); | 1156 ConstZeroBase = (0 == RealOffset); |
| 1150 } else if (0 != Offset) { | 1157 } else if (0 != Offset) { |
| 1151 auto *Addr = makeVariable(Ice::getPointerType()); | 1158 auto *Addr = makeVariable(Ice::getPointerType()); |
| 1152 auto *OffsetConstant = Ctx->getConstantInt32(Offset); | 1159 auto *OffsetConstant = Ctx->getConstantInt32(Offset); |
| 1153 Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, | 1160 Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, |
| 1154 Addr, Base, OffsetConstant)); | 1161 Addr, Base, OffsetConstant)); |
| 1155 | 1162 |
| 1156 Base = Addr; | 1163 Base = Addr; |
| 1157 } | 1164 } |
| 1158 | 1165 |
| 1166 // Do the bounds check. | |
| 1167 // | |
| 1168 // TODO (eholk): Add a command line argument to control whether bounds | |
| 1169 // checks are inserted, and maybe add a way to duplicate bounds checks to | |
| 1170 // get a better sense of the overhead. | |
| 1159 if (!llvm::dyn_cast<ConstantInteger32>(Base)) { | 1171 if (!llvm::dyn_cast<ConstantInteger32>(Base)) { |
| 1160 auto *ClampedAddr = makeVariable(Ice::getPointerType()); | 1172 // TODO (eholk): creating a new basic block on every memory access is |
| 1173 // terrible (see https://goto.google.com/aqydy). Try adding a new | |
|
Jim Stichnoth
2016/04/23 16:23:04
Use a link that is externally viewable...
Eric Holk
2016/04/25 19:53:03
I thought goto was, but just to be safe, I switche
| |
| 1174 // instruction that encapsulates this "abort if false" pattern. | |
| 1175 auto *CheckPassed = Func->makeNode(); | |
| 1176 auto *CheckFailed = getAbortTarget(); | |
| 1177 | |
| 1178 auto *Check = makeVariable(IceType_i1); | |
| 1179 Control()->appendInst(InstIcmp::create(Func, InstIcmp::Ult, Check, Base, | |
| 1180 Ctx->getConstantInt32(MemSize))); | |
| 1161 Control()->appendInst( | 1181 Control()->appendInst( |
| 1162 InstArithmetic::create(Func, InstArithmetic::And, ClampedAddr, Base, | 1182 InstBr::create(Func, Check, CheckPassed, CheckFailed)); |
| 1163 Ctx->getConstantInt32(MemSize - 1))); | 1183 |
| 1164 Base = ClampedAddr; | 1184 *ControlPtr = OperandNode(CheckPassed); |
| 1165 } | 1185 } |
| 1166 | 1186 |
| 1167 Ice::Operand *RealAddr = nullptr; | 1187 Ice::Operand *RealAddr = nullptr; |
| 1168 auto MemBase = Ctx->getConstantSym(0, Ctx->getGlobalString("WASM_MEMORY")); | 1188 auto MemBase = Ctx->getConstantSym(0, Ctx->getGlobalString("WASM_MEMORY")); |
| 1169 if (!ConstZeroBase) { | 1189 if (!ConstZeroBase) { |
| 1170 auto RealAddrV = Func->makeVariable(Ice::getPointerType()); | 1190 auto RealAddrV = Func->makeVariable(Ice::getPointerType()); |
| 1171 Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, | 1191 Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, |
| 1172 RealAddrV, Base, MemBase)); | 1192 RealAddrV, Base, MemBase)); |
| 1173 | 1193 |
| 1174 RealAddr = RealAddrV; | 1194 RealAddr = RealAddrV; |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1254 void set_effect_ptr(Node *Effect) { this->EffectPtr = Effect; } | 1274 void set_effect_ptr(Node *Effect) { this->EffectPtr = Effect; } |
| 1255 | 1275 |
| 1256 private: | 1276 private: |
| 1257 wasm::ModuleEnv *Module; | 1277 wasm::ModuleEnv *Module; |
| 1258 Node *ControlPtr; | 1278 Node *ControlPtr; |
| 1259 Node *EffectPtr; | 1279 Node *EffectPtr; |
| 1260 | 1280 |
| 1261 class Cfg *Func; | 1281 class Cfg *Func; |
| 1262 GlobalContext *Ctx; | 1282 GlobalContext *Ctx; |
| 1263 | 1283 |
| 1284 CfgNode *AbortTarget = nullptr; | |
| 1285 | |
| 1264 SizeT NextArg = 0; | 1286 SizeT NextArg = 0; |
| 1265 | 1287 |
| 1266 CfgUnorderedMap<Operand *, InstPhi *> PhiMap; | 1288 CfgUnorderedMap<Operand *, InstPhi *> PhiMap; |
| 1267 CfgUnorderedMap<Operand *, CfgNode *> DefNodeMap; | 1289 CfgUnorderedMap<Operand *, CfgNode *> DefNodeMap; |
| 1268 | 1290 |
| 1269 InstPhi *getDefiningInst(Operand *Op) const { | 1291 InstPhi *getDefiningInst(Operand *Op) const { |
| 1270 const auto &Iter = PhiMap.find(Op); | 1292 const auto &Iter = PhiMap.find(Op); |
| 1271 if (Iter == PhiMap.end()) { | 1293 if (Iter == PhiMap.end()) { |
| 1272 return nullptr; | 1294 return nullptr; |
| 1273 } | 1295 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 1290 } | 1312 } |
| 1291 | 1313 |
| 1292 CfgNode *getDefNode(Operand *Op) const { | 1314 CfgNode *getDefNode(Operand *Op) const { |
| 1293 const auto &Iter = DefNodeMap.find(Op); | 1315 const auto &Iter = DefNodeMap.find(Op); |
| 1294 if (Iter == DefNodeMap.end()) { | 1316 if (Iter == DefNodeMap.end()) { |
| 1295 return nullptr; | 1317 return nullptr; |
| 1296 } | 1318 } |
| 1297 return Iter->second; | 1319 return Iter->second; |
| 1298 } | 1320 } |
| 1299 | 1321 |
| 1322 CfgNode *getAbortTarget() { | |
| 1323 if (!AbortTarget) { | |
| 1324 AbortTarget = Func->makeNode(); | |
| 1325 // TODO(eholk): This should probably actually call abort instead. | |
| 1326 AbortTarget->appendInst(InstUnreachable::create(Func)); | |
| 1327 } | |
| 1328 | |
| 1329 return AbortTarget; | |
| 1330 } | |
| 1331 | |
| 1300 template <typename F = std::function<void(Ostream &)>> void log(F Fn) const { | 1332 template <typename F = std::function<void(Ostream &)>> void log(F Fn) const { |
| 1301 if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_Wasm)) { | 1333 if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_Wasm)) { |
| 1302 Fn(Ctx->getStrDump()); | 1334 Fn(Ctx->getStrDump()); |
| 1303 Ctx->getStrDump().flush(); | 1335 Ctx->getStrDump().flush(); |
| 1304 } | 1336 } |
| 1305 } | 1337 } |
| 1306 }; | 1338 }; |
| 1307 | 1339 |
| 1308 std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone, | 1340 std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone, |
| 1309 FunctionBody &Body) { | 1341 FunctionBody &Body) { |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1463 | 1495 |
| 1464 // Add the data | 1496 // Add the data |
| 1465 WasmMemory->addInitializer(VariableDeclaration::DataInitializer::create( | 1497 WasmMemory->addInitializer(VariableDeclaration::DataInitializer::create( |
| 1466 Globals.get(), reinterpret_cast<const char *>(Module->module_start) + | 1498 Globals.get(), reinterpret_cast<const char *>(Module->module_start) + |
| 1467 Seg.source_offset, | 1499 Seg.source_offset, |
| 1468 Seg.source_size)); | 1500 Seg.source_size)); |
| 1469 | 1501 |
| 1470 WritePtr += Seg.source_size; | 1502 WritePtr += Seg.source_size; |
| 1471 } | 1503 } |
| 1472 | 1504 |
| 1505 // Save the size of the initialized data in a global variable so the runtime | |
| 1506 // can use it to determine the initial heap break. | |
| 1507 auto *GlobalDataSize = VariableDeclaration::createExternal(Globals.get()); | |
| 1508 GlobalDataSize->setName(Ctx->getGlobalString("WASM_DATA_SIZE")); | |
| 1509 GlobalDataSize->addInitializer(VariableDeclaration::DataInitializer::create( | |
| 1510 Globals.get(), reinterpret_cast<const char *>(&WritePtr), | |
| 1511 sizeof(WritePtr))); | |
| 1512 | |
| 1473 // Pad the rest with zeros | 1513 // Pad the rest with zeros |
| 1474 SizeT DataSize = Module->min_mem_pages * WASM_PAGE_SIZE; | 1514 SizeT DataSize = Module->min_mem_pages * WASM_PAGE_SIZE; |
| 1475 if (WritePtr < DataSize) { | 1515 if (WritePtr < DataSize) { |
| 1476 WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create( | 1516 WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create( |
| 1477 Globals.get(), DataSize - WritePtr)); | 1517 Globals.get(), DataSize - WritePtr)); |
| 1478 } | 1518 } |
| 1479 | 1519 |
| 1520 // Save the number of pages for the runtime | |
| 1521 auto *GlobalNumPages = VariableDeclaration::createExternal(Globals.get()); | |
| 1522 GlobalNumPages->setName(Ctx->getGlobalString("WASM_NUM_PAGES")); | |
| 1523 GlobalNumPages->addInitializer(VariableDeclaration::DataInitializer::create( | |
| 1524 Globals.get(), reinterpret_cast<const char *>(&Module->min_mem_pages), | |
| 1525 sizeof(Module->min_mem_pages))); | |
| 1526 | |
| 1480 Globals->push_back(WasmMemory); | 1527 Globals->push_back(WasmMemory); |
| 1528 Globals->push_back(GlobalDataSize); | |
| 1529 Globals->push_back(GlobalNumPages); | |
| 1481 | 1530 |
| 1482 lowerGlobals(std::move(Globals)); | 1531 lowerGlobals(std::move(Globals)); |
| 1483 } | 1532 } |
| 1484 | 1533 |
| 1485 // Translate each function. | 1534 // Translate each function. |
| 1486 for (const auto Fn : Module->functions) { | 1535 for (const auto Fn : Module->functions) { |
| 1487 const auto FnName = getFunctionName(Module, Fn.func_index); | 1536 const auto FnName = getFunctionName(Module, Fn.func_index); |
| 1488 | 1537 |
| 1489 LOG(out << " " << Fn.func_index << ": " << FnName << "..."); | 1538 LOG(out << " " << Fn.func_index << ": " << FnName << "..."); |
| 1490 | 1539 |
| 1491 Body.sig = Fn.sig; | 1540 Body.sig = Fn.sig; |
| 1492 Body.base = Buffer.get(); | 1541 Body.base = Buffer.get(); |
| 1493 Body.start = Buffer.get() + Fn.code_start_offset; | 1542 Body.start = Buffer.get() + Fn.code_start_offset; |
| 1494 Body.end = Buffer.get() + Fn.code_end_offset; | 1543 Body.end = Buffer.get() + Fn.code_end_offset; |
| 1495 | 1544 |
| 1496 auto Func = translateFunction(&Zone, Body); | 1545 auto Func = translateFunction(&Zone, Body); |
| 1497 Func->setFunctionName(Ctx->getGlobalString(FnName)); | 1546 Func->setFunctionName(Ctx->getGlobalString(FnName)); |
| 1498 | 1547 |
| 1499 Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func))); | 1548 Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func))); |
| 1500 LOG(out << "done.\n"); | 1549 LOG(out << "done.\n"); |
| 1501 } | 1550 } |
| 1502 | 1551 |
| 1503 return; | 1552 return; |
| 1504 } | 1553 } |
| 1505 | 1554 |
| 1506 #endif // ALLOW_WASM | 1555 #endif // ALLOW_WASM |
| OLD | NEW |