feat: folding logical operations
This commit is contained in:
parent
92e8bcd866
commit
2260ccda25
24 changed files with 310 additions and 151 deletions
221
src/Sema.zig
221
src/Sema.zig
|
|
@ -35,19 +35,41 @@ pub const Value = struct {
|
|||
ip_index: InternPool.Index,
|
||||
|
||||
pub const Unwrapped = union(enum) {
|
||||
bool: bool,
|
||||
int: i64,
|
||||
float: f64,
|
||||
|
||||
fn toFloat(v: Value.Unwrapped) f64 {
|
||||
pub fn toFloat(v: Unwrapped) f64 {
|
||||
return switch (v) {
|
||||
.int => |i| @floatFromInt(i),
|
||||
.float => |f| f,
|
||||
.bool => |boolean| @floatFromInt(@intFromBool(boolean)),
|
||||
.int => |int| @floatFromInt(int),
|
||||
.float => |float| float,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isTruthy(v: Unwrapped) bool {
|
||||
return switch (v) {
|
||||
//.null => false,
|
||||
.bool => |boolean| boolean,
|
||||
.int => |int| int != 0,
|
||||
.float => |float| float != 0.0,
|
||||
//.str => true,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn coerce(value: Unwrapped) Unwrapped {
|
||||
return switch (value) {
|
||||
.int => value,
|
||||
.float => value,
|
||||
.bool => |boolean| .{ .int = if (boolean) 1 else 0 },
|
||||
//else => null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn unwrap(value: Value, ip: *InternPool) Unwrapped {
|
||||
switch (ip.values.items[@intFromEnum(value.toInterned())]) {
|
||||
.bool => |boolean| return .{ .bool = boolean },
|
||||
.int => |int| return .{ .int = int },
|
||||
.float => |float| return .{ .float = @bitCast(float) },
|
||||
.str => @panic("String unwrapping not implemented!"),
|
||||
|
|
@ -279,43 +301,90 @@ pub const Builder = struct {
|
|||
}
|
||||
};
|
||||
|
||||
fn foldArith(
|
||||
lhs: Value.Unwrapped,
|
||||
rhs: Value.Unwrapped,
|
||||
op: Story.Opcode,
|
||||
) !Value.Unwrapped {
|
||||
const l = lhs.coerce();
|
||||
const r = rhs.coerce();
|
||||
if (l == .int and r == .int) {
|
||||
return switch (op) {
|
||||
.add => .{ .int = l.int +% r.int },
|
||||
.sub => .{ .int = l.int -% r.int },
|
||||
.mul => .{ .int = l.int *% r.int },
|
||||
.div => if (r.int == 0)
|
||||
error.DivisionByZero
|
||||
else
|
||||
.{ .int = @divTrunc(l.int, r.int) },
|
||||
.mod => if (r.int == 0)
|
||||
error.DivisionByZero
|
||||
else
|
||||
.{ .int = @mod(l.int, r.int) },
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
const lf = l.toFloat();
|
||||
const rf = r.toFloat();
|
||||
return switch (op) {
|
||||
.add => .{ .float = lf + rf },
|
||||
.sub => .{ .float = lf - rf },
|
||||
.mul => .{ .float = lf * rf },
|
||||
.div => if (rf == 0.0)
|
||||
error.DivisionByZero
|
||||
else
|
||||
.{ .float = lf / rf },
|
||||
.mod => if (rf == 0.0)
|
||||
error.DivisionByZero
|
||||
else
|
||||
.{ .float = @mod(lf, rf) },
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn foldCmp(
|
||||
lhs: Value.Unwrapped,
|
||||
rhs: Value.Unwrapped,
|
||||
op: Story.Opcode,
|
||||
) !Value.Unwrapped {
|
||||
switch (op) {
|
||||
.cmp_eq => return .{ .bool = std.meta.eql(lhs, rhs) },
|
||||
.cmp_neq => return .{ .bool = !std.meta.eql(lhs, rhs) },
|
||||
else => {},
|
||||
}
|
||||
|
||||
const lf = lhs.coerce().toFloat();
|
||||
const rf = rhs.coerce().toFloat();
|
||||
const result = switch (op) {
|
||||
.cmp_lt => lf < rf,
|
||||
.cmp_gt => lf > rf,
|
||||
.cmp_lte => lf <= rf,
|
||||
.cmp_gte => lf >= rf,
|
||||
else => unreachable,
|
||||
};
|
||||
return .{ .bool = result };
|
||||
}
|
||||
|
||||
fn foldConstant(
|
||||
lhs: Value.Unwrapped,
|
||||
rhs: Value.Unwrapped,
|
||||
op: Story.Opcode,
|
||||
) !Value.Unwrapped {
|
||||
if (lhs == .int and rhs == .int) {
|
||||
switch (op) {
|
||||
.add => return .{ .int = lhs.int +% rhs.int },
|
||||
.sub => return .{ .int = lhs.int -% rhs.int },
|
||||
.mul => return .{ .int = lhs.int *% rhs.int },
|
||||
.div => {
|
||||
if (rhs.int == 0)
|
||||
return error.DivisionByZero;
|
||||
return .{ .int = @divTrunc(lhs.int, rhs.int) };
|
||||
},
|
||||
.mod => if (rhs.int == 0)
|
||||
return error.DivisionByZero
|
||||
else
|
||||
return .{ .int = @mod(lhs.int, rhs.int) },
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const l = lhs.toFloat();
|
||||
const r = rhs.toFloat();
|
||||
switch (op) {
|
||||
.add => return .{ .float = l + r },
|
||||
.sub => return .{ .float = l - r },
|
||||
.mul => return .{ .float = l * r },
|
||||
.div => if (r == 0.0)
|
||||
return error.DivisionByZero
|
||||
else
|
||||
return .{ .float = l / r },
|
||||
.mod => if (r == 0.0)
|
||||
return error.DivisionByZero
|
||||
else
|
||||
return .{ .float = @mod(l, r) },
|
||||
.add,
|
||||
.sub,
|
||||
.mul,
|
||||
.div,
|
||||
.mod,
|
||||
=> return foldArith(lhs, rhs, op),
|
||||
.cmp_eq,
|
||||
.cmp_neq,
|
||||
.cmp_lt,
|
||||
.cmp_gt,
|
||||
.cmp_lte,
|
||||
.cmp_gte,
|
||||
=> return foldCmp(lhs, rhs, op),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
@ -351,20 +420,27 @@ fn irUnaryOp(
|
|||
//const lhs_src: SrcLoc = .{ .src_offset = 0 };
|
||||
//try sema.analyzeArithmeticArg(builder, lhs, lhs_src);
|
||||
|
||||
if (sema.resolveValue(lhs)) |lhs_value| {
|
||||
const lhs_unwrapped = lhs_value.unwrap(ip);
|
||||
switch (lhs_unwrapped) {
|
||||
.int => |int| {
|
||||
if (sema.resolveValue(lhs)) |lhs_info| {
|
||||
switch (lhs_info.unwrap(ip)) {
|
||||
.bool => |boolean| {
|
||||
const new_value = switch (op) {
|
||||
.not => return error.TypeError,
|
||||
.not => !boolean,
|
||||
.neg => return error.TypeError,
|
||||
else => unreachable,
|
||||
};
|
||||
return .{ .value = ip.getOrPutBool(new_value) };
|
||||
},
|
||||
.int => |int| {
|
||||
const new_value: i64 = switch (op) {
|
||||
.not => if (int > 0) 0 else 1,
|
||||
.neg => -int,
|
||||
else => unreachable,
|
||||
};
|
||||
return .{ .value = try ip.getOrPutInt(gpa, new_value) };
|
||||
},
|
||||
.float => |float| {
|
||||
const new_value = switch (op) {
|
||||
.not => return error.TypeError,
|
||||
const new_value: f64 = switch (op) {
|
||||
.not => if (float > 0.0) 0.0 else 1.0,
|
||||
.neg => -float,
|
||||
else => unreachable,
|
||||
};
|
||||
|
|
@ -384,8 +460,9 @@ fn irBinaryOp(
|
|||
inst: Ir.Inst.Index,
|
||||
op: Story.Opcode,
|
||||
) InnerError!ValueInfo {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.bin;
|
||||
const gpa = sema.gpa;
|
||||
const ip = &sema.module.intern_pool;
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.bin;
|
||||
const lhs = sema.resolveInst(data.lhs);
|
||||
const rhs = sema.resolveInst(data.rhs);
|
||||
//const lhs_src: SrcLoc = .{ .src_offset = 0 };
|
||||
|
|
@ -395,16 +472,13 @@ fn irBinaryOp(
|
|||
|
||||
if (sema.resolveValue(lhs)) |lhs_value| {
|
||||
if (sema.resolveValue(rhs)) |rhs_value| {
|
||||
const lhs_unwrap = lhs_value.unwrap(ip);
|
||||
const rhs_unwrap = rhs_value.unwrap(ip);
|
||||
switch (try foldConstant(lhs_unwrap, rhs_unwrap, op)) {
|
||||
.int => |int| return .{
|
||||
.value = try ip.getOrPutInt(sema.gpa, int),
|
||||
},
|
||||
.float => |float| return .{
|
||||
.value = try ip.getOrPutFloat(sema.gpa, float),
|
||||
},
|
||||
}
|
||||
const lhs_coerced = lhs_value.unwrap(ip).coerce();
|
||||
const rhs_coerced = rhs_value.unwrap(ip).coerce();
|
||||
return switch (try foldConstant(lhs_coerced, rhs_coerced, op)) {
|
||||
.bool => |boolean| .{ .value = ip.getOrPutBool(boolean) },
|
||||
.int => |int| .{ .value = try ip.getOrPutInt(gpa, int) },
|
||||
.float => |float| .{ .value = try ip.getOrPutFloat(gpa, float) },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -414,6 +488,40 @@ fn irBinaryOp(
|
|||
return .none;
|
||||
}
|
||||
|
||||
fn irLogicalOp(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
inst: Ir.Inst.Index,
|
||||
logical_or: bool,
|
||||
) InnerError!ValueInfo {
|
||||
const ip = &sema.module.intern_pool;
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.bin;
|
||||
const lhs = sema.resolveInst(data.lhs);
|
||||
const rhs = sema.resolveInst(data.rhs);
|
||||
|
||||
if (sema.resolveValue(lhs)) |lhs_info| {
|
||||
const lhs_value = lhs_info.unwrap(ip);
|
||||
if (sema.resolveValue(rhs)) |_| {
|
||||
return if (logical_or)
|
||||
if (lhs_value.isTruthy()) lhs else rhs
|
||||
else if (!lhs_value.isTruthy()) lhs else rhs;
|
||||
}
|
||||
|
||||
if (logical_or and lhs_value.isTruthy()) return lhs;
|
||||
if (!logical_or and !lhs_value.isTruthy()) return lhs;
|
||||
try builder.ensureLoad(rhs);
|
||||
return .none;
|
||||
}
|
||||
|
||||
const else_label = try builder.addLabel();
|
||||
try builder.ensureLoad(lhs);
|
||||
try builder.addFixup(if (logical_or) .jmp_t else .jmp_f, else_label);
|
||||
try builder.addByteOp(.pop);
|
||||
try builder.ensureLoad(rhs);
|
||||
builder.setLabel(else_label);
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn irDeclRef(
|
||||
sema: *Sema,
|
||||
builder: *Builder,
|
||||
|
|
@ -672,7 +780,6 @@ fn irDivert(
|
|||
sema.gpa,
|
||||
extra.data.field_name_start,
|
||||
);
|
||||
std.debug.print("Target: {any}\n", .{target});
|
||||
const e = try sema.lookupInNamespace(target.namespace.?, ip_index, callee_src);
|
||||
switch (e.tag) {
|
||||
.knot => {
|
||||
|
|
@ -782,12 +889,10 @@ fn analyzeBodyInner(
|
|||
.mod => try irBinaryOp(sema, builder, inst, .mod),
|
||||
.neg => try irUnaryOp(sema, builder, inst, .neg),
|
||||
.not => try irUnaryOp(sema, builder, inst, .not),
|
||||
.bool_and => try irLogicalOp(sema, builder, inst, false),
|
||||
.bool_or => try irLogicalOp(sema, builder, inst, true),
|
||||
.cmp_eq => try irBinaryOp(sema, builder, inst, .cmp_eq),
|
||||
.cmp_neq => blk: {
|
||||
const val = try irBinaryOp(sema, builder, inst, .cmp_eq);
|
||||
try builder.addByteOp(.not);
|
||||
break :blk val;
|
||||
},
|
||||
.cmp_neq => try irBinaryOp(sema, builder, inst, .cmp_neq),
|
||||
.cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt),
|
||||
.cmp_lte => try irBinaryOp(sema, builder, inst, .cmp_lte),
|
||||
.cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue