compiler: Check SSA edge patch selection

50f94c5d7e20dd763a860dfc337ff555455c6ff64c588f7990753aa23f4a07a6
Alexis Sellier committed ago 1 parent 5a3af878
lib/std/lang/lower.rad +37 -15
3224 3224
) where 'arena: 'phase, 'phase: 'function {
3225 3225
    let allocator = alloc::arenaAllocator(self.arena);
3226 3226
    // Get mutable block data by block id.
3227 3227
    let data = &mut self.blockData[*from];
3228 3228
    let ix = data.instrs.len - 1; // The terminator is always the last instruction.
3229 +
    patchEdgeArgs(&mut data.instrs[ix], target, paramIdx, val, allocator);
3230 +
}
3229 3231
3232 +
/// Patch all edges in a terminator that pass a value to the target block.
3233 +
fn patchEdgeArgs(instr: &mut il::Instr, target: u32, paramIdx: u32, val: il::Val, allocator: alloc::Allocator) {
3230 3234
    // TODO: We shouldn't need to use a mutable subscript here, given that the
3231 3235
    // fields are already mutable.
3232 -
    match &mut data.instrs[ix] {
3236 +
    match instr {
3233 3237
        case il::Instr::Jmp { args, .. } => {
3234 -
            set *args = growArgs(*args, paramIdx + 1, allocator);
3235 -
            set args[paramIdx] = val;
3238 +
            unsafe {
3239 +
                set *args = growArgs(*args, paramIdx + 1, allocator);
3240 +
                set args[paramIdx] = val;
3241 +
            }
3236 3242
        }
3237 3243
        case il::Instr::Br { thenTarget, thenArgs, elseTarget, elseArgs, .. } => {
3238 3244
            // Nb. both branches could target the same block (e.g. `if cond { x } else { x }`).
3239 3245
            if *thenTarget == target {
3240 -
                set *thenArgs = growArgs(*thenArgs, paramIdx + 1, allocator);
3241 -
                set thenArgs[paramIdx] = val;
3246 +
                unsafe {
3247 +
                    set *thenArgs = growArgs(*thenArgs, paramIdx + 1, allocator);
3248 +
                    set thenArgs[paramIdx] = val;
3249 +
                }
3242 3250
            }
3243 3251
            if *elseTarget == target {
3244 -
                set *elseArgs = growArgs(*elseArgs, paramIdx + 1, allocator);
3245 -
                set elseArgs[paramIdx] = val;
3252 +
                unsafe {
3253 +
                    set *elseArgs = growArgs(*elseArgs, paramIdx + 1, allocator);
3254 +
                    set elseArgs[paramIdx] = val;
3255 +
                }
3246 3256
            }
3247 3257
        }
3248 3258
        case il::Instr::Switch { defaultTarget, defaultArgs, cases, .. } => {
3249 3259
            if *defaultTarget == target {
3250 -
                set *defaultArgs = growArgs(*defaultArgs, paramIdx + 1, allocator);
3251 -
                set defaultArgs[paramIdx] = val;
3252 -
            }
3253 -
            let idx = paramIdx + 1;
3254 -
            for ci in 0..cases.len {
3255 -
                if cases[ci].target == target {
3256 -
                    set cases[ci].args = growArgs(cases[ci].args, idx, allocator);
3257 -
                    set cases[ci].args[paramIdx] = val;
3260 +
                unsafe {
3261 +
                    set *defaultArgs = growArgs(*defaultArgs, paramIdx + 1, allocator);
3262 +
                    set defaultArgs[paramIdx] = val;
3258 3263
                }
3259 3264
            }
3265 +
            unsafe {
3266 +
                patchSwitchArgs(*cases, target, paramIdx, val, allocator);
3267 +
            }
3260 3268
        }
3261 3269
        else => {
3262 3270
            // Other terminators (e.g. `Ret`, `Unreachable`) don't have successor blocks.
3263 3271
        }
3264 3272
    }
3265 3273
}
3266 3274
3275 +
/// Patch every switch case that passes a value to the target block.
3276 +
fn patchSwitchArgs(cases: &mut [il::SwitchCase], target: u32, paramIdx: u32, val: il::Val, allocator: alloc::Allocator) {
3277 +
    let capacity = paramIdx + 1;
3278 +
    for i in 0..cases.len {
3279 +
        let branch = &mut cases[i];
3280 +
        if branch.target == target {
3281 +
            unsafe {
3282 +
                set branch.args = growArgs(branch.args, capacity, allocator);
3283 +
                set branch.args[paramIdx] = val;
3284 +
            }
3285 +
        }
3286 +
    }
3287 +
}
3288 +
3267 3289
/// Grow an args array to hold at least the given capacity.
3268 3290
unsafe fn growArgs(args: *unsafe mut [il::Val], capacity: u32, allocator: alloc::Allocator) -> *unsafe mut [il::Val] {
3269 3291
    if args.len >= capacity {
3270 3292
        return args;
3271 3293
    }