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 Base; | |
|
Jim Stichnoth
2016/04/22 20:48:54
Another possibility is GlobalContext::getConstantZ
Eric Holk
2016/04/22 22:32:48
Returning 0 is better. It means that even this der
| |
| 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. | |
|
Jim Stichnoth
2016/04/22 20:48:54
Maybe in a separate CL, but can you add a flag to
Eric Holk
2016/04/22 22:32:48
I added a TODO.
| |
| 1159 if (!llvm::dyn_cast<ConstantInteger32>(Base)) { | 1167 if (!llvm::dyn_cast<ConstantInteger32>(Base)) { |
| 1160 auto *ClampedAddr = makeVariable(Ice::getPointerType()); | 1168 auto *CheckPassed = Func->makeNode(); |
| 1169 auto *CheckFailed = getAbortTarget(); | |
| 1170 | |
| 1171 auto *Check = makeVariable(IceType_i1); | |
| 1172 Control()->appendInst(InstIcmp::create(Func, InstIcmp::Ult, Check, Base, | |
| 1173 Ctx->getConstantInt32(MemSize))); | |
| 1161 Control()->appendInst( | 1174 Control()->appendInst( |
| 1162 InstArithmetic::create(Func, InstArithmetic::And, ClampedAddr, Base, | 1175 InstBr::create(Func, Check, CheckPassed, CheckFailed)); |
| 1163 Ctx->getConstantInt32(MemSize - 1))); | 1176 |
| 1164 Base = ClampedAddr; | 1177 *ControlPtr = OperandNode(CheckPassed); |
|
Jim Stichnoth
2016/04/22 20:48:54
This one is definitely for a separate CL.
I think
Eric Holk
2016/04/22 22:32:48
I added a TODO, with a link to this comment.
| |
| 1165 } | 1178 } |
| 1166 | 1179 |
| 1167 Ice::Operand *RealAddr = nullptr; | 1180 Ice::Operand *RealAddr = nullptr; |
| 1168 auto MemBase = Ctx->getConstantSym(0, Ctx->getGlobalString("WASM_MEMORY")); | 1181 auto MemBase = Ctx->getConstantSym(0, Ctx->getGlobalString("WASM_MEMORY")); |
| 1169 if (!ConstZeroBase) { | 1182 if (!ConstZeroBase) { |
| 1170 auto RealAddrV = Func->makeVariable(Ice::getPointerType()); | 1183 auto RealAddrV = Func->makeVariable(Ice::getPointerType()); |
| 1171 Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, | 1184 Control()->appendInst(InstArithmetic::create(Func, InstArithmetic::Add, |
| 1172 RealAddrV, Base, MemBase)); | 1185 RealAddrV, Base, MemBase)); |
| 1173 | 1186 |
| 1174 RealAddr = RealAddrV; | 1187 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; } | 1267 void set_effect_ptr(Node *Effect) { this->EffectPtr = Effect; } |
| 1255 | 1268 |
| 1256 private: | 1269 private: |
| 1257 wasm::ModuleEnv *Module; | 1270 wasm::ModuleEnv *Module; |
| 1258 Node *ControlPtr; | 1271 Node *ControlPtr; |
| 1259 Node *EffectPtr; | 1272 Node *EffectPtr; |
| 1260 | 1273 |
| 1261 class Cfg *Func; | 1274 class Cfg *Func; |
| 1262 GlobalContext *Ctx; | 1275 GlobalContext *Ctx; |
| 1263 | 1276 |
| 1277 CfgNode *AbortTarget = nullptr; | |
| 1278 | |
| 1264 SizeT NextArg = 0; | 1279 SizeT NextArg = 0; |
| 1265 | 1280 |
| 1266 CfgUnorderedMap<Operand *, InstPhi *> PhiMap; | 1281 CfgUnorderedMap<Operand *, InstPhi *> PhiMap; |
| 1267 CfgUnorderedMap<Operand *, CfgNode *> DefNodeMap; | 1282 CfgUnorderedMap<Operand *, CfgNode *> DefNodeMap; |
| 1268 | 1283 |
| 1269 InstPhi *getDefiningInst(Operand *Op) const { | 1284 InstPhi *getDefiningInst(Operand *Op) const { |
| 1270 const auto &Iter = PhiMap.find(Op); | 1285 const auto &Iter = PhiMap.find(Op); |
| 1271 if (Iter == PhiMap.end()) { | 1286 if (Iter == PhiMap.end()) { |
| 1272 return nullptr; | 1287 return nullptr; |
| 1273 } | 1288 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 1290 } | 1305 } |
| 1291 | 1306 |
| 1292 CfgNode *getDefNode(Operand *Op) const { | 1307 CfgNode *getDefNode(Operand *Op) const { |
| 1293 const auto &Iter = DefNodeMap.find(Op); | 1308 const auto &Iter = DefNodeMap.find(Op); |
| 1294 if (Iter == DefNodeMap.end()) { | 1309 if (Iter == DefNodeMap.end()) { |
| 1295 return nullptr; | 1310 return nullptr; |
| 1296 } | 1311 } |
| 1297 return Iter->second; | 1312 return Iter->second; |
| 1298 } | 1313 } |
| 1299 | 1314 |
| 1315 CfgNode *getAbortTarget() { | |
|
Karl
2016/04/22 19:18:14
Does this need an atomic or a mutex to avoid multi
Jim Stichnoth
2016/04/22 20:48:54
I don't think there's any concurrency at this leve
Eric Holk
2016/04/22 21:02:10
I think unless we have plans to parallelize Wasm d
| |
| 1316 if (!AbortTarget) { | |
| 1317 AbortTarget = Func->makeNode(); | |
|
Jim Stichnoth
2016/04/22 20:48:54
I think it's best if you can somehow force this no
Eric Holk
2016/04/22 22:32:48
Do you know a good strategy for doing this off han
Jim Stichnoth
2016/04/23 16:23:04
I would probably just do a post-pass that heavy-ha
| |
| 1318 // TODO(eholk): This should probably actually call abort instead. | |
| 1319 AbortTarget->appendInst(InstUnreachable::create(Func)); | |
| 1320 } | |
| 1321 | |
| 1322 return AbortTarget; | |
| 1323 } | |
| 1324 | |
| 1300 template <typename F = std::function<void(Ostream &)>> void log(F Fn) const { | 1325 template <typename F = std::function<void(Ostream &)>> void log(F Fn) const { |
| 1301 if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_Wasm)) { | 1326 if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_Wasm)) { |
| 1302 Fn(Ctx->getStrDump()); | 1327 Fn(Ctx->getStrDump()); |
| 1303 Ctx->getStrDump().flush(); | 1328 Ctx->getStrDump().flush(); |
| 1304 } | 1329 } |
| 1305 } | 1330 } |
| 1306 }; | 1331 }; |
| 1307 | 1332 |
| 1308 std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone, | 1333 std::unique_ptr<Cfg> WasmTranslator::translateFunction(Zone *Zone, |
| 1309 FunctionBody &Body) { | 1334 FunctionBody &Body) { |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1463 | 1488 |
| 1464 // Add the data | 1489 // Add the data |
| 1465 WasmMemory->addInitializer(VariableDeclaration::DataInitializer::create( | 1490 WasmMemory->addInitializer(VariableDeclaration::DataInitializer::create( |
| 1466 Globals.get(), reinterpret_cast<const char *>(Module->module_start) + | 1491 Globals.get(), reinterpret_cast<const char *>(Module->module_start) + |
| 1467 Seg.source_offset, | 1492 Seg.source_offset, |
| 1468 Seg.source_size)); | 1493 Seg.source_size)); |
| 1469 | 1494 |
| 1470 WritePtr += Seg.source_size; | 1495 WritePtr += Seg.source_size; |
| 1471 } | 1496 } |
| 1472 | 1497 |
| 1498 // Save the size of the initialized data in a global variable so the runtime | |
| 1499 // can use it to determine the initial heap break. | |
| 1500 auto *GlobalDataSize = VariableDeclaration::createExternal(Globals.get()); | |
| 1501 GlobalDataSize->setName(Ctx->getGlobalString("WASM_DATA_SIZE")); | |
| 1502 GlobalDataSize->addInitializer(VariableDeclaration::DataInitializer::create( | |
| 1503 Globals.get(), reinterpret_cast<const char *>(&WritePtr), | |
| 1504 sizeof(WritePtr))); | |
| 1505 | |
| 1473 // Pad the rest with zeros | 1506 // Pad the rest with zeros |
| 1474 SizeT DataSize = Module->min_mem_pages * WASM_PAGE_SIZE; | 1507 SizeT DataSize = Module->min_mem_pages * WASM_PAGE_SIZE; |
| 1475 if (WritePtr < DataSize) { | 1508 if (WritePtr < DataSize) { |
| 1476 WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create( | 1509 WasmMemory->addInitializer(VariableDeclaration::ZeroInitializer::create( |
| 1477 Globals.get(), DataSize - WritePtr)); | 1510 Globals.get(), DataSize - WritePtr)); |
| 1478 } | 1511 } |
| 1479 | 1512 |
| 1513 // Save the number of pages for the runtime | |
| 1514 auto *GlobalNumPages = VariableDeclaration::createExternal(Globals.get()); | |
| 1515 GlobalNumPages->setName(Ctx->getGlobalString("WASM_NUM_PAGES")); | |
| 1516 GlobalNumPages->addInitializer(VariableDeclaration::DataInitializer::create( | |
| 1517 Globals.get(), reinterpret_cast<const char *>(&Module->min_mem_pages), | |
| 1518 sizeof(Module->min_mem_pages))); | |
| 1519 | |
| 1480 Globals->push_back(WasmMemory); | 1520 Globals->push_back(WasmMemory); |
| 1521 Globals->push_back(GlobalDataSize); | |
| 1522 Globals->push_back(GlobalNumPages); | |
| 1481 | 1523 |
| 1482 lowerGlobals(std::move(Globals)); | 1524 lowerGlobals(std::move(Globals)); |
| 1483 } | 1525 } |
| 1484 | 1526 |
| 1485 // Translate each function. | 1527 // Translate each function. |
| 1486 for (const auto Fn : Module->functions) { | 1528 for (const auto Fn : Module->functions) { |
| 1487 const auto FnName = getFunctionName(Module, Fn.func_index); | 1529 const auto FnName = getFunctionName(Module, Fn.func_index); |
| 1488 | 1530 |
| 1489 LOG(out << " " << Fn.func_index << ": " << FnName << "..."); | 1531 LOG(out << " " << Fn.func_index << ": " << FnName << "..."); |
| 1490 | 1532 |
| 1491 Body.sig = Fn.sig; | 1533 Body.sig = Fn.sig; |
| 1492 Body.base = Buffer.get(); | 1534 Body.base = Buffer.get(); |
| 1493 Body.start = Buffer.get() + Fn.code_start_offset; | 1535 Body.start = Buffer.get() + Fn.code_start_offset; |
| 1494 Body.end = Buffer.get() + Fn.code_end_offset; | 1536 Body.end = Buffer.get() + Fn.code_end_offset; |
| 1495 | 1537 |
| 1496 auto Func = translateFunction(&Zone, Body); | 1538 auto Func = translateFunction(&Zone, Body); |
| 1497 Func->setFunctionName(Ctx->getGlobalString(FnName)); | 1539 Func->setFunctionName(Ctx->getGlobalString(FnName)); |
| 1498 | 1540 |
| 1499 Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func))); | 1541 Ctx->optQueueBlockingPush(makeUnique<CfgOptWorkItem>(std::move(Func))); |
| 1500 LOG(out << "done.\n"); | 1542 LOG(out << "done.\n"); |
| 1501 } | 1543 } |
| 1502 | 1544 |
| 1503 return; | 1545 return; |
| 1504 } | 1546 } |
| 1505 | 1547 |
| 1506 #endif // ALLOW_WASM | 1548 #endif // ALLOW_WASM |
| OLD | NEW |