mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 08:45:52 +00:00
Sema: implement arithmetic
This commit is contained in:
parent
07c204393f
commit
5769c963e0
@ -1667,6 +1667,9 @@ pub const SrcLoc = struct {
|
||||
.node_offset_asm_ret_ty,
|
||||
.node_offset_if_cond,
|
||||
.node_offset_anyframe_type,
|
||||
.node_offset_bin_op,
|
||||
.node_offset_bin_lhs,
|
||||
.node_offset_bin_rhs,
|
||||
=> src_loc.container.decl.container.file_scope,
|
||||
};
|
||||
}
|
||||
@ -1722,6 +1725,9 @@ pub const SrcLoc = struct {
|
||||
.node_offset_asm_ret_ty => @panic("TODO"),
|
||||
.node_offset_if_cond => @panic("TODO"),
|
||||
.node_offset_anyframe_type => @panic("TODO"),
|
||||
.node_offset_bin_op => @panic("TODO"),
|
||||
.node_offset_bin_lhs => @panic("TODO"),
|
||||
.node_offset_bin_rhs => @panic("TODO"),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1846,6 +1852,20 @@ pub const LazySrcLoc = union(enum) {
|
||||
/// to the type expression.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_anyframe_type: i32,
|
||||
/// The source location points to a binary expression, such as `a + b`, found
|
||||
/// by taking this AST node index offset from the containing Decl AST node.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_bin_op: i32,
|
||||
/// The source location points to the LHS of a binary expression, found
|
||||
/// by taking this AST node index offset from the containing Decl AST node,
|
||||
/// which points to a binary expression AST node. Next, nagivate to the LHS.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_bin_lhs: i32,
|
||||
/// The source location points to the RHS of a binary expression, found
|
||||
/// by taking this AST node index offset from the containing Decl AST node,
|
||||
/// which points to a binary expression AST node. Next, nagivate to the RHS.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_bin_rhs: i32,
|
||||
|
||||
/// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
|
||||
pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
|
||||
@ -1877,6 +1897,9 @@ pub const LazySrcLoc = union(enum) {
|
||||
.node_offset_asm_ret_ty,
|
||||
.node_offset_if_cond,
|
||||
.node_offset_anyframe_type,
|
||||
.node_offset_bin_op,
|
||||
.node_offset_bin_lhs,
|
||||
.node_offset_bin_rhs,
|
||||
=> .{
|
||||
.container = .{ .decl = scope.srcDecl().? },
|
||||
.lazy = lazy,
|
||||
@ -1914,6 +1937,9 @@ pub const LazySrcLoc = union(enum) {
|
||||
.node_offset_asm_ret_ty,
|
||||
.node_offset_if_cond,
|
||||
.node_offset_anyframe_type,
|
||||
.node_offset_bin_op,
|
||||
.node_offset_bin_lhs,
|
||||
.node_offset_bin_rhs,
|
||||
=> .{
|
||||
.container = .{ .decl = decl },
|
||||
.lazy = lazy,
|
||||
|
||||
105
src/Sema.zig
105
src/Sema.zig
@ -2419,17 +2419,18 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
if (true) @panic("TODO rework with zir-memory-layout in mind");
|
||||
|
||||
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
|
||||
const src: LazySrcLoc = .todo;
|
||||
const lhs = try sema.resolveInst(bin_inst.lhs);
|
||||
const rhs = try sema.resolveInst(bin_inst.rhs);
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
|
||||
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const lhs = try sema.resolveInst(extra.lhs);
|
||||
const rhs = try sema.resolveInst(extra.rhs);
|
||||
|
||||
const instructions = &[_]*Inst{ lhs, rhs };
|
||||
const resolved_type = try sema.resolvePeerTypes(block, instructions);
|
||||
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs.src);
|
||||
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs.src);
|
||||
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
|
||||
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
|
||||
|
||||
const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
|
||||
resolved_type.elemType()
|
||||
@ -2455,8 +2456,9 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
|
||||
|
||||
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
|
||||
const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
|
||||
const zir_tags = block.sema.code.instructions.items(.tag);
|
||||
|
||||
if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
|
||||
if (!is_int and !(is_float and floatOpAllowed(zir_tags[inst]))) {
|
||||
return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
|
||||
}
|
||||
|
||||
@ -2468,71 +2470,56 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
|
||||
.val = Value.initTag(.undef),
|
||||
});
|
||||
}
|
||||
return sema.analyzeInstComptimeOp(block, scalar_type, inst, lhs_val, rhs_val);
|
||||
// incase rhs is 0, simply return lhs without doing any calculations
|
||||
// TODO Once division is implemented we should throw an error when dividing by 0.
|
||||
if (rhs_val.compareWithZero(.eq)) {
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = scalar_type,
|
||||
.val = lhs_val,
|
||||
});
|
||||
}
|
||||
|
||||
const value = switch (zir_tags[inst]) {
|
||||
.add => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intAdd(sema.arena, lhs_val, rhs_val)
|
||||
else
|
||||
try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val);
|
||||
break :blk val;
|
||||
},
|
||||
.sub => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intSub(sema.arena, lhs_val, rhs_val)
|
||||
else
|
||||
try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val);
|
||||
break :blk val;
|
||||
},
|
||||
else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tags[inst])}),
|
||||
};
|
||||
|
||||
log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tags[inst]), lhs_val, rhs_val, value });
|
||||
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = scalar_type,
|
||||
.val = value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
const ir_tag: Inst.Tag = switch (inst.base.tag) {
|
||||
const ir_tag: Inst.Tag = switch (zir_tags[inst]) {
|
||||
.add => .add,
|
||||
.addwrap => .addwrap,
|
||||
.sub => .sub,
|
||||
.subwrap => .subwrap,
|
||||
.mul => .mul,
|
||||
.mulwrap => .mulwrap,
|
||||
else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}),
|
||||
else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tags[inst])}),
|
||||
};
|
||||
|
||||
return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
|
||||
}
|
||||
|
||||
/// Analyzes operands that are known at comptime
|
||||
fn analyzeInstComptimeOp(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
res_type: Type,
|
||||
inst: zir.Inst.Index,
|
||||
lhs_val: Value,
|
||||
rhs_val: Value,
|
||||
) InnerError!*Inst {
|
||||
if (true) @panic("TODO rework analyzeInstComptimeOp for zir-memory-layout");
|
||||
|
||||
// incase rhs is 0, simply return lhs without doing any calculations
|
||||
// TODO Once division is implemented we should throw an error when dividing by 0.
|
||||
if (rhs_val.compareWithZero(.eq)) {
|
||||
return sema.mod.constInst(sema.arena, inst.base.src, .{
|
||||
.ty = res_type,
|
||||
.val = lhs_val,
|
||||
});
|
||||
}
|
||||
const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
|
||||
|
||||
const value = switch (inst.base.tag) {
|
||||
.add => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intAdd(sema.arena, lhs_val, rhs_val)
|
||||
else
|
||||
try Module.floatAdd(sema.arena, res_type, inst.base.src, lhs_val, rhs_val);
|
||||
break :blk val;
|
||||
},
|
||||
.sub => blk: {
|
||||
const val = if (is_int)
|
||||
try Module.intSub(sema.arena, lhs_val, rhs_val)
|
||||
else
|
||||
try Module.floatSub(sema.arena, res_type, inst.base.src, lhs_val, rhs_val);
|
||||
break :blk val;
|
||||
},
|
||||
else => return sema.mod.fail(&block.base, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}),
|
||||
};
|
||||
|
||||
log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value });
|
||||
|
||||
return sema.mod.constInst(sema.arena, inst.base.src, .{
|
||||
.ty = res_type,
|
||||
.val = value,
|
||||
});
|
||||
}
|
||||
|
||||
fn zirDerefNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -3000,19 +3000,18 @@ fn as(
|
||||
lhs: ast.Node.Index,
|
||||
rhs: ast.Node.Index,
|
||||
) InnerError!zir.Inst.Ref {
|
||||
if (true) @panic("TODO update for zir-memory-layout");
|
||||
const dest_type = try typeExpr(mod, scope, lhs);
|
||||
switch (rl) {
|
||||
.none, .discard, .ref, .ty => {
|
||||
const result = try expr(mod, scope, .{ .ty = dest_type }, rhs);
|
||||
return rvalue(mod, scope, rl, result);
|
||||
return rvalue(mod, scope, rl, result, node);
|
||||
},
|
||||
|
||||
.ptr => |result_ptr| {
|
||||
return asRlPtr(mod, scope, rl, src, result_ptr, rhs, dest_type);
|
||||
return asRlPtr(mod, scope, rl, result_ptr, rhs, dest_type);
|
||||
},
|
||||
.block_ptr => |block_scope| {
|
||||
return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, rhs, dest_type);
|
||||
return asRlPtr(mod, scope, rl, block_scope.rl_ptr, rhs, dest_type);
|
||||
},
|
||||
|
||||
.bitcasted_ptr => |bitcasted_ptr| {
|
||||
@ -3030,7 +3029,6 @@ fn asRlPtr(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
src: usize,
|
||||
result_ptr: zir.Inst.Ref,
|
||||
operand_node: ast.Node.Index,
|
||||
dest_type: zir.Inst.Ref,
|
||||
@ -3038,32 +3036,35 @@ fn asRlPtr(
|
||||
// Detect whether this expr() call goes into rvalue() to store the result into the
|
||||
// result location. If it does, elide the coerce_result_ptr instruction
|
||||
// as well as the store instruction, instead passing the result as an rvalue.
|
||||
const parent_gz = scope.getGenZir();
|
||||
|
||||
var as_scope: Scope.GenZir = .{
|
||||
.parent = scope,
|
||||
.decl = scope.ownerDecl().?,
|
||||
.arena = scope.arena(),
|
||||
.zir_code = parent_gz.zir_code,
|
||||
.force_comptime = scope.isComptime(),
|
||||
.instructions = .{},
|
||||
};
|
||||
defer as_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
as_scope.rl_ptr = try addZIRBinOp(mod, &as_scope.base, src, .coerce_result_ptr, dest_type, result_ptr);
|
||||
as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
|
||||
const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
|
||||
const parent_zir = &scope.getGenZir().instructions;
|
||||
const parent_zir = &parent_gz.instructions;
|
||||
if (as_scope.rvalue_rl_count == 1) {
|
||||
// Busted! This expression didn't actually need a pointer.
|
||||
const zir_tags = parent_gz.zir_code.instructions.items(.tag);
|
||||
const zir_datas = parent_gz.zir_code.instructions.items(.data);
|
||||
const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
|
||||
try parent_zir.ensureCapacity(mod.gpa, expected_len);
|
||||
for (as_scope.instructions.items) |src_inst| {
|
||||
if (src_inst == as_scope.rl_ptr.?) continue;
|
||||
if (src_inst.castTag(.store_to_block_ptr)) |store| {
|
||||
if (store.positionals.lhs == as_scope.rl_ptr.?) continue;
|
||||
if (src_inst == as_scope.rl_ptr) continue;
|
||||
if (zir_tags[src_inst] == .store_to_block_ptr) {
|
||||
if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
|
||||
}
|
||||
parent_zir.appendAssumeCapacity(src_inst);
|
||||
}
|
||||
assert(parent_zir.items.len == expected_len);
|
||||
const casted_result = try addZIRBinOp(mod, scope, dest_type.src, .as, dest_type, result);
|
||||
return rvalue(mod, scope, rl, casted_result);
|
||||
const casted_result = try parent_gz.addBin(.as, dest_type, result);
|
||||
return rvalue(mod, scope, rl, casted_result, operand_node);
|
||||
} else {
|
||||
try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items);
|
||||
return result;
|
||||
|
||||
55
src/zir.zig
55
src/zir.zig
@ -524,6 +524,7 @@ pub const Inst = struct {
|
||||
cmp_neq,
|
||||
/// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
|
||||
/// as type coercion from the new element type to the old element type.
|
||||
/// Uses the `bin` union field.
|
||||
/// LHS is destination element type, RHS is result pointer.
|
||||
coerce_result_ptr,
|
||||
/// Emit an error message and fail compilation.
|
||||
@ -1327,33 +1328,12 @@ const Writer = struct {
|
||||
const tag = tags[inst];
|
||||
try stream.print("= {s}(", .{@tagName(tags[inst])});
|
||||
switch (tag) {
|
||||
.add,
|
||||
.addwrap,
|
||||
.array_cat,
|
||||
.array_mul,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.sub,
|
||||
.subwrap,
|
||||
.array_type,
|
||||
.bit_and,
|
||||
.bit_or,
|
||||
.as,
|
||||
.bool_and,
|
||||
.bool_or,
|
||||
.@"break",
|
||||
.cmp_lt,
|
||||
.cmp_lte,
|
||||
.cmp_eq,
|
||||
.cmp_gte,
|
||||
.cmp_gt,
|
||||
.cmp_neq,
|
||||
.coerce_result_ptr,
|
||||
.div,
|
||||
.mod_rem,
|
||||
.shl,
|
||||
.shr,
|
||||
.xor,
|
||||
.elem_ptr,
|
||||
.elem_val,
|
||||
.intcast,
|
||||
@ -1447,6 +1427,29 @@ const Writer = struct {
|
||||
.suspend_block,
|
||||
=> try self.writePlNode(stream, inst),
|
||||
|
||||
.add,
|
||||
.addwrap,
|
||||
.array_cat,
|
||||
.array_mul,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.sub,
|
||||
.subwrap,
|
||||
.bool_and,
|
||||
.bool_or,
|
||||
.cmp_lt,
|
||||
.cmp_lte,
|
||||
.cmp_eq,
|
||||
.cmp_gte,
|
||||
.cmp_gt,
|
||||
.cmp_neq,
|
||||
.div,
|
||||
.mod_rem,
|
||||
.shl,
|
||||
.shr,
|
||||
.xor,
|
||||
=> try self.writePlNodeBin(stream, inst),
|
||||
|
||||
.as_node => try self.writeAs(stream, inst),
|
||||
|
||||
.breakpoint,
|
||||
@ -1589,6 +1592,16 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data;
|
||||
try self.writeInstRef(stream, extra.lhs);
|
||||
try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, extra.rhs);
|
||||
try stream.writeAll(") ");
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Inst.As, inst_data.payload_index).data;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user