From 7f492b3ffc7361b94b32085117ecde1915565eea Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Tue, 29 Oct 2024 10:15:20 +0000 Subject: [PATCH] Fix a crash in try-catch Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- src/jit/Analysis.cpp | 40 ++++++++++++-- src/jit/RegisterAlloc.cpp | 11 ++-- test/jit/trycatch-dep.wast | 103 +++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 test/jit/trycatch-dep.wast diff --git a/src/jit/Analysis.cpp b/src/jit/Analysis.cpp index c15502598..afcb38c59 100644 --- a/src/jit/Analysis.cpp +++ b/src/jit/Analysis.cpp @@ -96,10 +96,13 @@ void DependencyGenContext::update(size_t dependencyStart, size_t id, size_t excl && maxDistance[dependencyStart / size] <= id); for (auto it : param) { - VariableRef ref = variableList->variables.size(); + if (variableList != nullptr) { + // Construct new variables. + VariableRef ref = variableList->variables.size(); - dependencies[dependencyStart + offset].insert(VARIABLE_SET(ref, Variable)); - variableList->variables.push_back(VariableList::Variable(VARIABLE_SET(offset, Instruction::Offset), 0, id)); + dependencies[dependencyStart + offset].insert(VARIABLE_SET(ref, Variable)); + variableList->variables.push_back(VariableList::Variable(VARIABLE_SET(offset, Instruction::Offset), 0, id)); + } offset += STACK_OFFSET(valueStackAllocatedSize(it)); } @@ -287,6 +290,7 @@ void JITCompiler::buildVariables(uint32_t requiredStackSize) size_t nextId = 0; size_t nextTryBlock = m_tryBlockStart; + // Create variables for each result or external values. for (InstructionListItem* item = m_first; item != nullptr; item = item->next()) { item->m_id = ++nextId; @@ -322,6 +326,7 @@ void JITCompiler::buildVariables(uint32_t requiredStackSize) DependencyGenContext dependencyCtx(dependencySize, requiredStackSize); bool updateDeps = true; + std::vector activeTryBlocks; m_variableList = new VariableList(variableCount, requiredStackSize); nextTryBlock = m_tryBlockStart; @@ -365,11 +370,16 @@ void JITCompiler::buildVariables(uint32_t requiredStackSize) } } + activeTryBlocks.push_back(nextTryBlock); nextTryBlock++; } while (nextTryBlock < tryBlocks().size() && tryBlocks()[nextTryBlock].start == label); } + if (label->info() & Label::kHasCatchInfo) { + activeTryBlocks.pop_back(); + } + for (size_t i = 0; i < requiredStackSize; ++i) { dependencyCtx.currentDependencies[i] = VARIABLE_SET_PTR(label); dependencyCtx.currentOptions[i] = 0; @@ -424,7 +434,28 @@ void JITCompiler::buildVariables(uint32_t requiredStackSize) continue; } - if (instr->opcode() == ByteCode::ThrowOpcode || instr->opcode() == ByteCode::UnreachableOpcode) { + if (activeTryBlocks.size() > 0 && (instr->group() == Instruction::Call || instr->opcode() == ByteCode::ThrowOpcode)) { + // Every call or throw may jump to any active catch block. Future + // optimizations could reduce these (e.g. a throw can be converted + // to a jump if its target catch block is in the same function). + for (auto blockIt : activeTryBlocks) { + for (auto it : tryBlocks()[blockIt].catchBlocks) { + if (it.tagIndex == std::numeric_limits::max()) { + dependencyCtx.update(it.u.handler->m_dependencyStart, instr->id()); + } else { + TagType* tagType = module()->tagType(it.tagIndex); + const ValueTypeVector& param = module()->functionType(tagType->sigIndex())->param(); + Label* catchLabel = it.u.handler; + + dependencyCtx.update(catchLabel->m_dependencyStart, catchLabel->id(), + STACK_OFFSET(it.stackSizeToBe), param, nullptr); + } + } + } + } + + if (instr->opcode() == ByteCode::ThrowOpcode || instr->opcode() == ByteCode::UnreachableOpcode + || instr->opcode() == ByteCode::EndOpcode) { updateDeps = false; continue; } @@ -495,6 +526,7 @@ void JITCompiler::buildVariables(uint32_t requiredStackSize) } ASSERT(variableCount == m_variableList->variables.size()); + ASSERT(activeTryBlocks.size() == 0); // Phase 2: the indirect instruction // references are computed for labels. diff --git a/src/jit/RegisterAlloc.cpp b/src/jit/RegisterAlloc.cpp index 6fbc063b3..c2f639de5 100644 --- a/src/jit/RegisterAlloc.cpp +++ b/src/jit/RegisterAlloc.cpp @@ -298,14 +298,16 @@ uint8_t RegisterSet::allocateRegisterPair(VariableList::Variable* variable, uint size_t freeReg = VariableList::kUnusedReg; uint16_t constraints = variable != nullptr ? variable->info : 0; size_t size = m_registers.size(); - size_t i = 0; + size_t minIndex = 0; if (constraints & VariableList::kIsCallback) { - i = m_savedStartIndex; + minIndex = m_savedStartIndex; } else if ((constraints & VariableList::kDestroysR0R1) && (m_regStatus & kIsInteger)) { - i = 2; + minIndex = 2; } + size_t i = minIndex; + while (i < size) { if (m_registers[i].rangeEnd == kUnassignedReg) { if (freeReg != VariableList::kUnusedReg) { @@ -317,7 +319,8 @@ uint8_t RegisterSet::allocateRegisterPair(VariableList::Variable* variable, uint VariableList::Variable* targetVariable = m_registers[i].variable; if (targetVariable->reg1 != targetVariable->reg2) { - if (targetVariable->rangeEnd > maxRangeEndPair) { + if (targetVariable->reg1 >= minIndex && targetVariable->reg2 >= minIndex + && targetVariable->rangeEnd > maxRangeEndPair) { maxRangeEndPair = targetVariable->rangeEnd; maxRangeIndexPair = i; } diff --git a/test/jit/trycatch-dep.wast b/test/jit/trycatch-dep.wast new file mode 100644 index 000000000..2a3168ea1 --- /dev/null +++ b/test/jit/trycatch-dep.wast @@ -0,0 +1,103 @@ +(module + (memory 1) + + (tag $except0 (param i32 i64)) + (tag $except1 (param f32 f64)) + (tag $except3) + + (func $throw1 (param i64) + i32.const 1234 + local.get 0 + throw $except0 + ) + + (func $throw2 (param i64) + f32.const 2345.0 + local.get 0 + f64.convert_i64_s + throw $except1 + ) + + (func $throw3 (param i64) + throw $except3 + ) + + (func (export "try1") (param i64) (result i64 i32) (local $l1 i64) (local $l2 i32) + i32.const 123456 + local.set $l2 + (try + (do + (try + (do + local.get 0 + local.set $l1 + + local.get $l1 + i32.wrap_i64 + i32.const 100 + i32.add + local.set $l2 + + i64.const 1000 + local.get $l1 + i64.lt_s + if + local.get $l1 + call $throw1 + end + + local.get $l1 + i64.const 500 + i64.gt_s + if + local.get $l1 + call $throw2 + end + + local.get $l1 + i64.const 250 + i64.gt_s + if + local.get $l1 + call $throw3 + end + + local.get $l1 + i64.const 17 + i64.sub + i32.const -678 + return + ) + (catch $except0 + local.set $l1 + i64.extend_i32_s + local.get $l1 + i64.add + local.set $l1 + ) + ) + ) + (catch $except1 + i64.trunc_sat_f64_s + local.set $l1 + + i64.trunc_sat_f32_s + local.get $l1 + i64.add + local.set $l1 + ) + (catch_all + i64.const 888 + local.set $l1 + ) + ) + + local.get $l1 + local.get $l2 + ) +) + +(assert_return (invoke "try1" (i64.const 99)) (i64.const 82) (i32.const -678)) +(assert_return (invoke "try1" (i64.const 2000)) (i64.const 3234) (i32.const 2100)) +(assert_return (invoke "try1" (i64.const 600)) (i64.const 2945) (i32.const 700)) +(assert_return (invoke "try1" (i64.const 300)) (i64.const 888) (i32.const 400))