94 lines
3.2 KiB
Zig
94 lines
3.2 KiB
Zig
const std = @import("std");
|
|
const cfg = @import("../cfg.zig");
|
|
const ids = @import("../id.zig");
|
|
const module_ir = @import("../module.zig");
|
|
|
|
pub const Error = std.mem.Allocator.Error || error{
|
|
InvalidBlock,
|
|
DefinitionDoesNotDominateUse,
|
|
};
|
|
|
|
const DominanceUseContext = struct {
|
|
module: *const module_ir.Module,
|
|
analysis: *const cfg,
|
|
function_id: ids.FunctionId,
|
|
use_block: ids.BlockId,
|
|
use_index: usize,
|
|
valid: bool = true,
|
|
};
|
|
|
|
pub fn validate(module: *const module_ir.Module, function_id: ids.FunctionId) Error!void {
|
|
var analysis = cfg.init(module.backingAllocator(), module, function_id) catch |err| switch (err) {
|
|
std.mem.Allocator.Error.OutOfMemory => return Error.OutOfMemory,
|
|
else => return Error.InvalidBlock,
|
|
};
|
|
defer analysis.deinit();
|
|
|
|
const function = module.functions.get(function_id).?;
|
|
for (function.blocks.items) |block_id| {
|
|
const block = module.blocks.get(block_id).?;
|
|
|
|
for (block.instructions.items, 0..) |instruction_id, instruction_index| {
|
|
const instruction = module.instructions.get(instruction_id).?;
|
|
var context: DominanceUseContext = .{
|
|
.module = module,
|
|
.analysis = &analysis,
|
|
.function_id = function_id,
|
|
.use_block = block_id,
|
|
.use_index = instruction_index,
|
|
};
|
|
|
|
instruction.operation.visitValueUses(&context, checkDominanceUse);
|
|
|
|
if (!context.valid)
|
|
return Error.DefinitionDoesNotDominateUse;
|
|
}
|
|
|
|
var context: DominanceUseContext = .{
|
|
.module = module,
|
|
.analysis = &analysis,
|
|
.function_id = function_id,
|
|
.use_block = block_id,
|
|
.use_index = block.instructions.items.len,
|
|
};
|
|
|
|
module_ir.visitTerminatorValueUses(block.terminator.?, &context, checkDominanceUse);
|
|
|
|
if (!context.valid)
|
|
return Error.DefinitionDoesNotDominateUse;
|
|
}
|
|
}
|
|
|
|
fn checkDominanceUse(context: *DominanceUseContext, value_id: ids.ValueId) void {
|
|
if (!context.valid)
|
|
return;
|
|
|
|
const value = context.module.values.get(value_id) orelse {
|
|
context.valid = false;
|
|
return;
|
|
};
|
|
|
|
context.valid = switch (value.definition) {
|
|
.constant, .undef => true,
|
|
.function_parameter => |definition| definition.function == context.function_id,
|
|
.block_parameter => |definition| context.analysis.dominates(definition.block, context.use_block),
|
|
.instruction => |instruction_id| blk: {
|
|
const definition = context.module.instructions.get(instruction_id) orelse break :blk false;
|
|
|
|
if (!context.analysis.dominates(definition.parent_block, context.use_block))
|
|
break :blk false;
|
|
|
|
if (definition.parent_block != context.use_block)
|
|
break :blk true;
|
|
|
|
const block = context.module.blocks.get(context.use_block) orelse break :blk false;
|
|
for (block.instructions.items, 0..) |candidate, definition_index| {
|
|
if (candidate == instruction_id)
|
|
break :blk definition_index < context.use_index;
|
|
}
|
|
|
|
break :blk false;
|
|
},
|
|
};
|
|
}
|