compiler: Detect terminators through checked instruction slices

415fdcf8a2c227b5d20827a881fec7f205f38d8b37bbdd0636dcadc0157b94d9
Alexis Sellier committed ago 1 parent 06628001
lib/std/lang/lower.rad +15 -16
2665 2665
    }
2666 2666
}
2667 2667
2668 2668
/// Emit a jump to target if the current block hasn't terminated, then seal the target block.
2669 2669
unsafe fn emitJmpAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2670 -
    if not blockHasTerminator(self) {
2670 +
    if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
2671 2671
        try emitJmp(self, target);
2672 2672
    }
2673 2673
    try sealBlock(self, target);
2674 2674
}
2675 2675
2676 -
/// Check if the current block already has a terminator instruction.
2677 -
unsafe fn blockHasTerminator 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> bool where 'arena: 'phase, 'phase: 'function {
2678 -
    let blk = getBlock(&self.blockData[..], currentBlock(self));
2679 -
    if blk.instrs.len == 0 {
2676 +
/// Check if a block's instruction list ends with a terminator.
2677 +
fn blockHasTerminator(instrs: &[il::Instr]) -> bool {
2678 +
    if instrs.len == 0 {
2680 2679
        return false;
2681 2680
    }
2682 -
    match blk.instrs[blk.instrs.len - 1] {
2681 +
    match instrs[instrs.len - 1] {
2683 2682
        case il::Instr::Ret { .. },
2684 2683
             il::Instr::Jmp { .. },
2685 2684
             il::Instr::Br { .. },
2686 2685
             il::Instr::Switch { .. },
2687 2686
             il::Instr::Unreachable =>
2705 2704
///     }
2706 2705
///
2707 2706
/// In the above example, the merge block stays `nil`, and no code is generated
2708 2707
/// after the `if`. The merge block is created on first use.
2709 2708
unsafe fn emitMergeIfUnterminated 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2710 -
    if not blockHasTerminator(self) {
2709 +
    if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
2711 2710
        if *mergeBlock == nil {
2712 2711
            set *mergeBlock = try createBlock(self, "merge");
2713 2712
        }
2714 2713
        let target = *mergeBlock else { throw LowerError::MissingTarget; };
2715 2714
        try emitJmp(self, target);
3676 3675
    }
3677 3676
    /// Lower function body.
3678 3677
    try lowerBlock(self, body);
3679 3678
3680 3679
    // Add implicit return if body doesn't diverge.
3681 -
    if not blockHasTerminator(self) {
3680 +
    if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
3682 3681
        if *self.fnType.returnType == resolver::Type::Never {
3683 3682
            emit(self, il::Instr::Unreachable);
3684 3683
        } else if self.fnType.throwList.len > 0 {
3685 3684
            if *self.fnType.returnType == resolver::Type::Void {
3686 3685
                // Implicit `void` return in throwing function: wrap in result success.
4080 4079
            let values = try allocVals(self, bindings.len);
4081 4080
            for bindingNode, i in bindings {
4082 4081
                let case ast::NodeValue::RegionBinding(binding) = bindingNode.value
4083 4082
                    else throw LowerError::ExpectedIdentifier;
4084 4083
                set values[i] = try lowerExpr(self, binding.value);
4085 -
                if blockHasTerminator(self) {
4084 +
                if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
4086 4085
                    exitVarScope(&mut self.vars, savedVarsLen);
4087 4086
                    return;
4088 4087
                }
4089 4088
            }
4090 4089
            for bindingNode, i in bindings {
4187 4186
    let savedVarsLen = enterVarScope(&self.vars);
4188 4187
    for stmt in blk.statements {
4189 4188
        try lowerNode(self, stmt);
4190 4189
4191 4190
        // If the statement diverges, further statements are unreachable.
4192 -
        if blockHasTerminator(self) {
4191 +
        if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
4193 4192
            exitVarScope(&mut self.vars, savedVarsLen);
4194 4193
            return;
4195 4194
        }
4196 4195
    }
4197 4196
    exitVarScope(&mut self.vars, savedVarsLen);
5373 5372
5374 5373
/// Lower a let binding.
5375 5374
unsafe fn lowerLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, l: ast::Let) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5376 5375
    // Evaluate value.
5377 5376
    let val = try lowerExpr(self, l.value);
5378 -
    if blockHasTerminator(self) {
5377 +
    if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
5379 5378
        return;
5380 5379
    }
5381 5380
    try bindLetValue(self, node, l, val);
5382 5381
}
5383 5382
5817 5816
        try emitJmpAndSeal(self, stepBlock);
5818 5817
        switchToBlock(self, stepBlock);
5819 5818
    }
5820 5819
    // Otherwise, emit increment directly in the current block,
5821 5820
    // saving the jump to a separate step block.
5822 -
    if not blockHasTerminator(self) {
5821 +
    if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
5823 5822
        match *iter {
5824 5823
            case ForIter::Range { valVar, valType, indexVar, .. } => {
5825 5824
                try emitIncrement(self, valVar, valType);
5826 5825
                if let idxVar = indexVar {
5827 5826
                    try emitIncrement(self, idxVar, il::Type::W32);
5958 5957
unsafe fn lowerReturnStmt 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, value: ?*ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function {
5959 5958
    let mut val = il::Val::Undef;
5960 5959
    if let expr = value {
5961 5960
        set val = try lowerExpr(self, expr);
5962 5961
    }
5963 -
    if blockHasTerminator(self) {
5962 +
    if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
5964 5963
        return;
5965 5964
    }
5966 5965
    set val = try applyCoercion(self, node, val);
5967 5966
    try emitRetVal(self, val);
5968 5967
}
6462 6461
    } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra {
6463 6462
        set resVal = try lowerMethodCall(self, t.expr, callExpr, method);
6464 6463
    } else {
6465 6464
        set resVal = try lowerCall(self, t.expr, callExpr);
6466 6465
    }
6467 -
    if blockHasTerminator(self) {
6466 +
    if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
6468 6467
        return il::Val::Undef;
6469 6468
    }
6470 6469
    let base = emitValToReg(self, resVal); // The result value.
6471 6470
    let tagReg = resultTagReg(self, base); // The result tag.
6472 6471
6890 6889
    emitLoadW64At(
6891 6890
        self, functionReg, vtableReg,
6892 6891
        (allocation.methodIndex * resolver::PTR_SIZE) as i32,
6893 6892
    );
6894 6893
    let value = try lowerCallArg(self, call.args[0], true);
6895 -
    if blockHasTerminator(self) {
6894 +
    if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
6896 6895
        return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef };
6897 6896
    }
6898 6897
    let slice = allocation.kind <> resolver::SessionAllocationKind::New;
6899 6898
    let mut count = il::Val::Imm(1);
6900 6899
    if allocation.kind == resolver::SessionAllocationKind::Copy {
6901 6900
        set count = loadSliceLen(self, emitValToReg(self, value));
6902 6901
    } else if allocation.kind == resolver::SessionAllocationKind::Fill {
6903 6902
        set count = try lowerExpr(self, call.args[1]);
6904 -
        if blockHasTerminator(self) {
6903 +
        if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) {
6905 6904
            return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef };
6906 6905
        }
6907 6906
    }
6908 6907
    let layout = resolver::getTypeLayout(*allocation.item);
6909 6908
    let size = layout.size if layout.size > 0 else 1;