External functions don't have bodies to inline anyway.
Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/605
}
}
+bool InlinePass::IsInlinableFunctionCall(const ir::Instruction* inst) {
+ if (inst->opcode() != SpvOp::SpvOpFunctionCall) return false;
+ const ir::Function* calleeFn =
+ id2function_[inst->GetSingleWordOperand(kSpvFunctionCallFunctionId)];
+ // We can only inline a function if it has blocks.
+ return calleeFn->cbegin() != calleeFn->cend();
+}
+
bool InlinePass::Inline(ir::Function* func) {
bool modified = false;
// Using block iterators here because of block erasures and insertions.
for (auto bi = func->begin(); bi != func->end(); bi++) {
for (auto ii = bi->begin(); ii != bi->end();) {
- if (ii->opcode() == SpvOp::SpvOpFunctionCall) {
+ if (IsInlinableFunctionCall(&*ii)) {
// Inline call.
std::vector<std::unique_ptr<ir::BasicBlock>> newBlocks;
std::vector<std::unique_ptr<ir::Instruction>> newVars;
const auto lastBlk = newBlocks.end() - 1;
const uint32_t firstId = (*firstBlk)->label_id();
const uint32_t lastId = (*lastBlk)->label_id();
- (*lastBlk)
- ->ForEachSuccessorLabel([&firstId, &lastId, this](uint32_t succ) {
+ (*lastBlk)->ForEachSuccessorLabel(
+ [&firstId, &lastId, this](uint32_t succ) {
ir::BasicBlock* sbp = this->id2block_[succ];
sbp->ForEachPhiInst([&firstId, &lastId](ir::Instruction* phi) {
phi->ForEachInId([&firstId, &lastId](uint32_t* id) {
// is returned. Formal parameters are trivially mapped to their actual
// parameters. Note that the first block in new_blocks retains the label
// of the original calling block. Also note that if an exit block is
- // created, it is the last block of new_blocks.
+ // created, it is the last block of new_blocks.
//
// Also return in new_vars additional OpVariable instructions required by
// and to be inserted into the caller function after the block at
ir::UptrVectorIterator<ir::Instruction> call_inst_itr,
ir::UptrVectorIterator<ir::BasicBlock> call_block_itr);
+ // Returns true if |inst| is a function call that can be inlined.
+ bool IsInlinableFunctionCall(const ir::Instruction* inst);
+
// Exhaustively inline all function calls in func as well as in
// all code that is inlined into func. Return true if func is modified.
bool Inline(ir::Function* func);
/* skip_nop = */ false, /* do_validate = */ true);
}
+TEST_F(InlineTest, ExternalFunctionIsNotInlined) {
+ // In particular, don't crash.
+ // See report https://github.com/KhronosGroup/SPIRV-Tools/issues/605
+ const std::string assembly =
+ R"(OpCapability Addresses
+OpCapability Kernel
+OpCapability Linkage
+OpMemoryModel Physical32 OpenCL
+OpEntryPoint Kernel %1 "entry_pt"
+OpDecorate %2 LinkageAttributes "external" Import
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%2 = OpFunction %void None %4
+OpFunctionEnd
+%1 = OpFunction %void None %4
+%5 = OpLabel
+%6 = OpFunctionCall %void %2
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::InlinePass>(assembly, assembly, false, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Empty modules