aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig9
-rw-r--r--src/Module.zig4
-rw-r--r--src/Sema.zig285
-rw-r--r--src/arch/arm/CodeGen.zig34
-rw-r--r--src/arch/arm/Mir.zig12
-rw-r--r--src/arch/arm/bits.zig77
-rw-r--r--src/codegen/llvm.zig2
-rw-r--r--src/type.zig2
8 files changed, 336 insertions, 89 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index b9ac0b5c43..e8d612fbc7 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -244,10 +244,14 @@ pub const ResultLoc = union(enum) {
fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
switch (rl) {
// In this branch there will not be any store_to_block_ptr instructions.
- .discard, .none, .ty, .coerced_ty, .ref => return .{
+ .none, .ty, .coerced_ty, .ref => return .{
.tag = .break_operand,
.elide_store_to_block_ptr_instructions = false,
},
+ .discard => return .{
+ .tag = .break_void,
+ .elide_store_to_block_ptr_instructions = false,
+ },
// The pointer got passed through to the sub-expressions, so we will use
// break_void here.
// In this branch there will not be any store_to_block_ptr instructions.
@@ -1766,6 +1770,9 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
// we assume the result location is written, and we break with void.
_ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
},
+ .discard => {
+ _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
+ },
else => {
_ = try parent_gz.addBreak(break_tag, block_inst, operand);
},
diff --git a/src/Module.zig b/src/Module.zig
index 048895e5bf..8fed3138e7 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -1125,6 +1125,8 @@ pub const Union = struct {
abi_align: Value,
/// Returns the field alignment, assuming the union is not packed.
+ /// Keep implementation in sync with `Sema.unionFieldAlignment`.
+ /// Prefer to call that function instead of this one during Sema.
pub fn normalAlignment(field: Field, target: Target) u32 {
if (field.abi_align.tag() == .abi_align_default) {
return field.ty.abiAlignment(target);
@@ -3762,7 +3764,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
// Note this resolves the type of the Decl, not the value; if this Decl
// is a struct, for example, this resolves `type` (which needs no resolution),
// not the struct itself.
- try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);
+ try sema.resolveTypeFully(&block_scope, src, decl_tv.ty);
const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);
diff --git a/src/Sema.zig b/src/Sema.zig
index 8e93d2525b..1fdc9e02eb 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -4494,6 +4494,10 @@ fn analyzeCall(
try sema.emitBackwardBranch(&child_block, call_src);
+ // Whether this call should be memoized, set to false if the call can mutate
+ // comptime state.
+ var should_memoize = true;
+
// This will have return instructions analyzed as break instructions to
// the block_inst above. Here we are performing "comptime/inline semantic analysis"
// for a function body, which means we must map the parameter ZIR instructions to
@@ -4527,6 +4531,7 @@ fn analyzeCall(
},
else => {},
}
+ should_memoize = should_memoize and !arg_val.isComptimeMutablePtr();
memoized_call_key.args[arg_i] = .{
.ty = param_ty,
.val = arg_val,
@@ -4552,6 +4557,7 @@ fn analyzeCall(
},
else => {},
}
+ should_memoize = should_memoize and !arg_val.isComptimeMutablePtr();
memoized_call_key.args[arg_i] = .{
.ty = sema.typeOf(uncasted_arg),
.val = arg_val,
@@ -4597,7 +4603,7 @@ fn analyzeCall(
// This `res2` is here instead of directly breaking from `res` due to a stage1
// bug generating invalid LLVM IR.
const res2: Air.Inst.Ref = res2: {
- if (is_comptime_call) {
+ if (should_memoize and is_comptime_call) {
if (mod.memoized_calls.get(memoized_call_key)) |result| {
const ty_inst = try sema.addType(fn_ret_ty);
try sema.air_values.append(gpa, result.val);
@@ -4621,7 +4627,7 @@ fn analyzeCall(
break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges);
};
- if (is_comptime_call) {
+ if (should_memoize and is_comptime_call) {
const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result);
// TODO: check whether any external comptime memory was mutated by the
@@ -9897,63 +9903,63 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Type)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Void => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Void)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Bool => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Bool)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.NoReturn => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.NoReturn)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.ComptimeFloat => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeFloat)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.ComptimeInt => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeInt)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Undefined => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Undefined)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Null => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Null)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.EnumLiteral => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.EnumLiteral)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Fn => {
@@ -10374,6 +10380,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
};
const union_ty = try sema.resolveTypeFields(block, src, ty);
+ try sema.resolveTypeLayout(block, src, ty); // Getting alignment requires type layout
+ const layout = union_ty.containerLayout();
+
const union_fields = union_ty.unionFields();
const union_field_vals = try fields_anon_decl.arena().alloc(Value, union_fields.count());
@@ -10392,13 +10401,18 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
};
const union_field_fields = try fields_anon_decl.arena().create([3]Value);
+ const alignment = switch (layout) {
+ .Auto, .Extern => try sema.unionFieldAlignment(block, src, field),
+ .Packed => 0,
+ };
+
union_field_fields.* = .{
// name: []const u8,
name_val,
// field_type: type,
try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty),
// alignment: comptime_int,
- try field.abi_align.copy(fields_anon_decl.arena()),
+ try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
};
field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), union_field_fields);
}
@@ -10429,7 +10443,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
// layout: ContainerLayout,
try Value.Tag.enum_field_index.create(
sema.arena,
- @enumToInt(union_ty.containerLayout()),
+ @enumToInt(layout),
),
// tag_type: ?type,
@@ -10467,6 +10481,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
};
const struct_ty = try sema.resolveTypeFields(block, src, ty);
+ try sema.resolveTypeLayout(block, src, ty); // Getting alignment requires type layout
const layout = struct_ty.containerLayout();
const struct_field_vals = fv: {
@@ -10975,8 +10990,7 @@ fn zirCondbr(
if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {
const body = if (cond_val.toBool()) then_body else else_body;
- _ = try sema.analyzeBody(parent_block, body);
- return always_noreturn;
+ return sema.analyzeBodyInner(parent_block, body);
}
const gpa = sema.gpa;
@@ -11837,6 +11851,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const operand = sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
+ try sema.resolveTypeLayout(block, operand_src, operand_ty);
const enum_ty = switch (operand_ty.zigTypeTag()) {
.EnumLiteral => {
const val = try sema.resolveConstValue(block, operand_src, operand);
@@ -11932,7 +11947,23 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
const ty = try Type.vector(sema.arena, len, try child_ty.copy(sema.arena));
return sema.addType(ty);
},
- .Float => return sema.fail(block, src, "TODO: Sema.zirReify for Float", .{}),
+ .Float => {
+ const struct_val = union_val.val.castTag(.@"struct").?.data;
+ // TODO use reflection instead of magic numbers here
+ // bits: comptime_int,
+ const bits_val = struct_val[0];
+
+ const bits = @intCast(u16, bits_val.toUnsignedInt());
+ const ty = switch (bits) {
+ 16 => Type.@"f16",
+ 32 => Type.@"f32",
+ 64 => Type.@"f64",
+ 80 => Type.@"f80",
+ 128 => Type.@"f128",
+ else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}),
+ };
+ return sema.addType(ty);
+ },
.Pointer => {
const struct_val = union_val.val.castTag(.@"struct").?.data;
// TODO use reflection instead of magic numbers here
@@ -11948,26 +11979,82 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
var buffer: Value.ToTypeBuffer = undefined;
const child_ty = child_val.toType(&buffer);
+ const ptr_size = size_val.toEnum(std.builtin.TypeInfo.Pointer.Size);
+
+ var actual_sentinel: ?Value = null;
if (!sentinel_val.isNull()) {
- return sema.fail(block, src, "TODO: implement zirReify for pointer with non-null sentinel", .{});
+ if (ptr_size == .One or ptr_size == .C) {
+ return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
+ }
+ const sentinel_ptr_val = sentinel_val.castTag(.opt_payload).?.data;
+ const ptr_ty = try Type.ptr(sema.arena, .{ .@"addrspace" = .generic, .pointee_type = child_ty });
+ actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
}
const ty = try Type.ptr(sema.arena, .{
- .size = size_val.toEnum(std.builtin.TypeInfo.Pointer.Size),
+ .size = ptr_size,
.mutable = !is_const_val.toBool(),
.@"volatile" = is_volatile_val.toBool(),
.@"align" = @intCast(u8, alignment_val.toUnsignedInt()), // TODO: Validate this value.
.@"addrspace" = address_space_val.toEnum(std.builtin.AddressSpace),
.pointee_type = try child_ty.copy(sema.arena),
.@"allowzero" = is_allowzero_val.toBool(),
- .sentinel = null,
+ .sentinel = actual_sentinel,
});
return sema.addType(ty);
},
- .Array => return sema.fail(block, src, "TODO: Sema.zirReify for Array", .{}),
+ .Array => {
+ const struct_val = union_val.val.castTag(.@"struct").?.data;
+ // TODO use reflection instead of magic numbers here
+ // len: comptime_int,
+ const len_val = struct_val[0];
+ // child: type,
+ const child_val = struct_val[1];
+ // sentinel: ?*const anyopaque,
+ const sentinel_val = struct_val[2];
+
+ const len = len_val.toUnsignedInt();
+ var buffer: Value.ToTypeBuffer = undefined;
+ const child_ty = try child_val.toType(&buffer).copy(sema.arena);
+ const sentinel = if (sentinel_val.castTag(.opt_payload)) |p| blk: {
+ const ptr_ty = try Type.ptr(sema.arena, .{ .@"addrspace" = .generic, .pointee_type = child_ty });
+ break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?;
+ } else null;
+
+ const ty = try Type.array(sema.arena, len, sentinel, child_ty);
+ return sema.addType(ty);
+ },
.Struct => return sema.fail(block, src, "TODO: Sema.zirReify for Struct", .{}),
- .Optional => return sema.fail(block, src, "TODO: Sema.zirReify for Optional", .{}),
- .ErrorUnion => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorUnion", .{}),
+ .Optional => {
+ const struct_val = union_val.val.castTag(.@"struct").?.data;
+ // TODO use reflection instead of magic numbers here
+ // child: type,
+ const child_val = struct_val[0];
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ const child_ty = try child_val.toType(&buffer).copy(sema.arena);
+
+ const ty = try Type.optional(sema.arena, child_ty);
+ return sema.addType(ty);
+ },
+ .ErrorUnion => {
+ const struct_val = union_val.val.castTag(.@"struct").?.data;
+ // TODO use reflection instead of magic numbers here
+ // error_set: type,
+ const error_set_val = struct_val[0];
+ // payload: type,
+ const payload_val = struct_val[1];
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ const error_set_ty = try error_set_val.toType(&buffer).copy(sema.arena);
+ const payload_ty = try payload_val.toType(&buffer).copy(sema.arena);
+
+ const ty = try Type.Tag.error_union.create(sema.arena, .{
+ .error_set = error_set_ty,
+ .payload = payload_ty,
+ });
+ return sema.addType(ty);
+ },
.ErrorSet => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorSet", .{}),
.Enum => return sema.fail(block, src, "TODO: Sema.zirReify for Enum", .{}),
.Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}),
@@ -12070,6 +12157,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const type_res = try sema.resolveType(block, src, extra.lhs);
try sema.checkPtrType(block, type_src, type_res);
+ _ = try sema.resolveTypeLayout(block, src, type_res.childType());
const ptr_align = type_res.ptrAlignment(sema.mod.getTarget());
if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
@@ -13066,9 +13154,59 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
}
fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirBuiltinCall", .{});
+ const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+ const call_src = inst_data.src();
+
+ const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index);
+ var func = sema.resolveInst(extra.data.callee);
+ const options = sema.resolveInst(extra.data.options);
+ const args = sema.resolveInst(extra.data.args);
+
+ const modifier: std.builtin.CallOptions.Modifier = modifier: {
+ const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
+ const coerced_options = try sema.coerce(block, export_options_ty, options, options_src);
+ const options_val = try sema.resolveConstValue(block, options_src, coerced_options);
+ const fields = options_val.castTag(.@"struct").?.data;
+ const struct_obj = export_options_ty.castTag(.@"struct").?.data;
+ const modifier_index = struct_obj.fields.getIndex("modifier").?;
+ const stack_index = struct_obj.fields.getIndex("stack").?;
+ if (!fields[stack_index].isNull()) {
+ return sema.fail(block, options_src, "TODO: implement @call with stack", .{});
+ }
+ break :modifier fields[modifier_index].toEnum(std.builtin.CallOptions.Modifier);
+ };
+
+ const args_ty = sema.typeOf(args);
+ if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) {
+ return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty});
+ }
+
+ var resolved_args: []Air.Inst.Ref = undefined;
+
+ // Desugar bound functions here
+ if (sema.typeOf(func).tag() == .bound_fn) {
+ const bound_func = try sema.resolveValue(block, func_src, func);
+ const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data;
+ func = bound_data.func_inst;
+ resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount() + 1);
+ resolved_args[0] = bound_data.arg0_inst;
+ for (resolved_args[1..]) |*resolved, i| {
+ resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty);
+ }
+ } else {
+ resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount());
+ for (resolved_args) |*resolved, i| {
+ resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty);
+ }
+ }
+
+ return sema.analyzeCall(block, func, func_src, call_src, modifier, false, resolved_args);
}
fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -14596,10 +14734,8 @@ fn tupleFieldVal(
field_name_src: LazySrcLoc,
tuple_ty: Type,
) CompileError!Air.Inst.Ref {
- const tuple = tuple_ty.castTag(.tuple).?.data;
-
if (mem.eql(u8, field_name, "len")) {
- return sema.addIntUnsigned(Type.usize, tuple.types.len);
+ return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount());
}
const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
@@ -14607,7 +14743,18 @@ fn tupleFieldVal(
tuple_ty, field_name, @errorName(err),
});
};
+ return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
+}
+fn tupleFieldValByIndex(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ tuple_byval: Air.Inst.Ref,
+ field_index: u32,
+ tuple_ty: Type,
+) CompileError!Air.Inst.Ref {
+ const tuple = tuple_ty.castTag(.tuple).?.data;
const field_ty = tuple.types[field_index];
if (tuple.values[field_index].tag() != .unreachable_value) {
@@ -17035,30 +17182,41 @@ fn cmpNumeric(
if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
if (lhs_val.isUndef())
return sema.addConstUndef(Type.bool);
- const is_unsigned = if (lhs_is_float) x: {
+ if (!rhs_is_signed) {
+ switch (lhs_val.orderAgainstZero()) {
+ .gt => {},
+ .eq => switch (op) { // LHS = 0, RHS is unsigned
+ .lte => return Air.Inst.Ref.bool_true,
+ .gt => return Air.Inst.Ref.bool_false,
+ else => {},
+ },
+ .lt => switch (op) { // LHS < 0, RHS is unsigned
+ .neq, .lt, .lte => return Air.Inst.Ref.bool_true,
+ .eq, .gt, .gte => return Air.Inst.Ref.bool_false,
+ },
+ }
+ }
+ if (lhs_is_float) {
var bigint_space: Value.BigIntSpace = undefined;
var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
defer bigint.deinit();
- const zcmp = lhs_val.orderAgainstZero();
if (lhs_val.floatHasFraction()) {
switch (op) {
.eq => return Air.Inst.Ref.bool_false,
.neq => return Air.Inst.Ref.bool_true,
else => {},
}
- if (zcmp == .lt) {
+ if (lhs_is_signed) {
try bigint.addScalar(bigint.toConst(), -1);
} else {
try bigint.addScalar(bigint.toConst(), 1);
}
}
lhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
+ } else {
lhs_bits = lhs_val.intBitCountTwosComp(target);
- break :x (lhs_val.orderAgainstZero() != .lt);
- };
- lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ }
+ lhs_bits += @boolToInt(!lhs_is_signed and dest_int_is_signed);
} else if (lhs_is_float) {
dest_float_type = lhs_ty;
} else {
@@ -17070,30 +17228,41 @@ fn cmpNumeric(
if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
if (rhs_val.isUndef())
return sema.addConstUndef(Type.bool);
- const is_unsigned = if (rhs_is_float) x: {
+ if (!lhs_is_signed) {
+ switch (rhs_val.orderAgainstZero()) {
+ .gt => {},
+ .eq => switch (op) { // RHS = 0, LHS is unsigned
+ .gte => return Air.Inst.Ref.bool_true,
+ .lt => return Air.Inst.Ref.bool_false,
+ else => {},
+ },
+ .lt => switch (op) { // RHS < 0, LHS is unsigned
+ .neq, .gt, .gte => return Air.Inst.Ref.bool_true,
+ .eq, .lt, .lte => return Air.Inst.Ref.bool_false,
+ },
+ }
+ }
+ if (rhs_is_float) {
var bigint_space: Value.BigIntSpace = undefined;
var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
defer bigint.deinit();
- const zcmp = rhs_val.orderAgainstZero();
if (rhs_val.floatHasFraction()) {
switch (op) {
.eq => return Air.Inst.Ref.bool_false,
.neq => return Air.Inst.Ref.bool_true,
else => {},
}
- if (zcmp == .lt) {
+ if (rhs_is_signed) {
try bigint.addScalar(bigint.toConst(), -1);
} else {
try bigint.addScalar(bigint.toConst(), 1);
}
}
rhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
+ } else {
rhs_bits = rhs_val.intBitCountTwosComp(target);
- break :x (rhs_val.orderAgainstZero() != .lt);
- };
- rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ }
+ rhs_bits += @boolToInt(!rhs_is_signed and dest_int_is_signed);
} else if (rhs_is_float) {
dest_float_type = rhs_ty;
} else {
@@ -17587,7 +17756,7 @@ fn resolvePeerTypes(
return chosen_ty;
}
-pub fn resolveTypeLayout(
+fn resolveTypeLayout(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -17603,6 +17772,10 @@ pub fn resolveTypeLayout(
.Optional => {
var buf: Type.Payload.ElemType = undefined;
const payload_ty = ty.optionalChild(&buf);
+ // In case of querying the ABI alignment of this optional, we will ask
+ // for hasRuntimeBits() of the payload type, so we need "requires comptime"
+ // to be known already before this function returns.
+ _ = try sema.typeRequiresComptime(block, src, payload_ty);
return sema.resolveTypeLayout(block, src, payload_ty);
},
.ErrorUnion => {
@@ -17634,6 +17807,13 @@ fn resolveStructLayout(
try sema.resolveTypeLayout(block, src, field.ty);
}
struct_obj.status = .have_layout;
+
+ // In case of querying the ABI alignment of this struct, we will ask
+ // for hasRuntimeBits() of each field, so we need "requires comptime"
+ // to be known already before this function returns.
+ for (struct_obj.fields.values()) |field| {
+ _ = try sema.typeRequiresComptime(block, src, field.ty);
+ }
}
// otherwise it's a tuple; no need to resolve anything
}
@@ -17660,7 +17840,7 @@ fn resolveUnionLayout(
union_obj.status = .have_layout;
}
-fn resolveTypeFully(
+pub fn resolveTypeFully(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -19187,6 +19367,21 @@ fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32
return ty.abiAlignment(target);
}
+/// Not valid to call for packed unions.
+/// Keep implementation in sync with `Module.Union.Field.normalAlignment`.
+fn unionFieldAlignment(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ field: Module.Union.Field,
+) !u32 {
+ if (field.abi_align.tag() == .abi_align_default) {
+ return sema.typeAbiAlignment(block, src, field.ty);
+ } else {
+ return @intCast(u32, field.abi_align.toUnsignedInt());
+ }
+}
+
/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
pub fn fnHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
const fn_info = ty.fnInfo();
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
index f4e97fa8b1..6cd4a1fd9f 100644
--- a/src/arch/arm/CodeGen.zig
+++ b/src/arch/arm/CodeGen.zig
@@ -433,7 +433,9 @@ fn gen(self: *Self) !void {
});
// exitlude jumps
- if (self.exitlude_jump_relocs.items.len == 1) {
+ const only_one_exitlude_jump = self.exitlude_jump_relocs.items.len == 1 and
+ self.exitlude_jump_relocs.items[0] == self.mir_instructions.len - 1;
+ if (only_one_exitlude_jump) {
// There is only one relocation. Hence,
// this relocation must be at the end of
// the code. Therefore, we can just delete
@@ -1066,7 +1068,17 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void {
fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+ const ptr = try self.resolveInst(bin_op.lhs);
+ const ptr_ty = self.air.typeOf(bin_op.lhs);
+ const len = try self.resolveInst(bin_op.rhs);
+ const len_ty = self.air.typeOf(bin_op.rhs);
+
+ const stack_offset = try self.allocMem(inst, 8, 8);
+ try self.genSetStack(ptr_ty, stack_offset + 4, ptr);
+ try self.genSetStack(len_ty, stack_offset, len);
+ break :result MCValue{ .stack_offset = stack_offset };
+ };
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
@@ -1359,6 +1371,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
const base_mcv: MCValue = switch (slice_mcv) {
.stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off + 4 }) },
+ .stack_argument_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_argument_offset = off + 4 }) },
else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}),
};
self.register_manager.freezeRegs(&.{base_mcv.register});
@@ -3854,9 +3867,17 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{
- self.target.cpu.arch,
- });
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+ const ptr_ty = self.air.typeOf(ty_op.operand);
+ const ptr = try self.resolveInst(ty_op.operand);
+ const array_ty = ptr_ty.childType();
+ const array_len = @intCast(u32, array_ty.arrayLenIncludingSentinel());
+
+ const stack_offset = try self.allocMem(inst, 8, 8);
+ try self.genSetStack(ptr_ty, stack_offset + 4, ptr);
+ try self.genSetStack(Type.initTag(.usize), stack_offset, .{ .immediate = array_len });
+ break :result MCValue{ .stack_offset = stack_offset };
+ };
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
@@ -4077,6 +4098,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
}
switch (typed_value.ty.zigTypeTag()) {
+ .Array => {
+ return self.lowerUnnamedConst(typed_value);
+ },
.Pointer => switch (typed_value.ty.ptrSize()) {
.Slice => {
return self.lowerUnnamedConst(typed_value);
diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig
index 7852a39885..5dcc3f7095 100644
--- a/src/arch/arm/Mir.zig
+++ b/src/arch/arm/Mir.zig
@@ -118,8 +118,6 @@ pub const Inst = struct {
/// All instructions have a 8-byte payload, which is contained within
/// this union. `Tag` determines which union field is active, as well as
/// how to interpret the data within.
- // TODO flatten down Data (remove use of tagged unions) to make it
- // 8 bytes only
pub const Data = union {
/// No additional data
///
@@ -231,11 +229,11 @@ pub const Inst = struct {
// Make sure we don't accidentally make instructions bigger than expected.
// Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
- // comptime {
- // if (builtin.mode != .Debug) {
- // assert(@sizeOf(Data) == 8);
- // }
- // }
+ comptime {
+ if (builtin.mode != .Debug) {
+ assert(@sizeOf(Data) == 8);
+ }
+ }
};
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig
index 568b7580c8..a23f99f789 100644
--- a/src/arch/arm/bits.zig
+++ b/src/arch/arm/bits.zig
@@ -469,6 +469,23 @@ pub const Instruction = union(enum) {
}
};
+ pub const AddressingMode = enum {
+ /// [<Rn>, <offset>]
+ ///
+ /// Address = Rn + offset
+ offset,
+ /// [<Rn>, <offset>]!
+ ///
+ /// Address = Rn + offset
+ /// Rn = Rn + offset
+ pre_index,
+ /// [<Rn>], <offset>
+ ///
+ /// Address = Rn
+ /// Rn = Rn + offset
+ post_index,
+ };
+
/// Represents the offset operand of a load or store
/// instruction. Data can be loaded from memory with either an
/// immediate offset or an offset that is stored in some register.
@@ -730,10 +747,9 @@ pub const Instruction = union(enum) {
rd: Register,
rn: Register,
offset: Offset,
- pre_index: bool,
+ mode: AddressingMode,
positive: bool,
byte_word: u1,
- write_back: bool,
load_store: u1,
) Instruction {
return Instruction{
@@ -743,10 +759,16 @@ pub const Instruction = union(enum) {
.rd = rd.id(),
.offset = offset.toU12(),
.load_store = load_store,
- .write_back = @boolToInt(write_back),
+ .write_back = switch (mode) {
+ .offset => 0b0,
+ .pre_index, .post_index => 0b1,
+ },
.byte_word = byte_word,
.up_down = @boolToInt(positive),
- .pre_post = @boolToInt(pre_index),
+ .pre_post = switch (mode) {
+ .offset, .pre_index => 0b1,
+ .post_index => 0b0,
+ },
.imm = @boolToInt(offset != .immediate),
},
};
@@ -754,9 +776,8 @@ pub const Instruction = union(enum) {
fn extraLoadStore(
cond: Condition,
- pre_index: bool,
+ mode: AddressingMode,
positive: bool,
- write_back: bool,
o1: u1,
op2: u2,
rn: Register,
@@ -780,10 +801,16 @@ pub const Instruction = union(enum) {
.rt = rt.id(),
.rn = rn.id(),
.o1 = o1,
- .write_back = @boolToInt(write_back),
+ .write_back = switch (mode) {
+ .offset => 0b0,
+ .pre_index, .post_index => 0b1,
+ },
.imm = @boolToInt(offset == .immediate),
.up_down = @boolToInt(positive),
- .pre_index = @boolToInt(pre_index),
+ .pre_index = switch (mode) {
+ .offset, .pre_index => 0b1,
+ .post_index => 0b0,
+ },
.cond = @enumToInt(cond),
},
};
@@ -1091,51 +1118,49 @@ pub const Instruction = union(enum) {
// Single data transfer
pub const OffsetArgs = struct {
- pre_index: bool = true,
+ mode: AddressingMode = .offset,
positive: bool = true,
offset: Offset,
- write_back: bool = false,
};
pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1);
+ return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 1);
}
pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1);
+ return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 1);
}
pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0);
+ return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 0);
}
pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
- return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
+ return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 0);
}
// Extra load/store
pub const ExtraLoadStoreOffsetArgs = struct {
- pre_index: bool = true,
+ mode: AddressingMode = .offset,
positive: bool = true,
offset: ExtraLoadStoreOffset,
- write_back: bool = false,
};
pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
- return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b0, 0b01, rn, rt, args.offset);
+ return extraLoadStore(cond, args.mode, args.positive, 0b0, 0b01, rn, rt, args.offset);
}
pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
- return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b01, rn, rt, args.offset);
+ return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b01, rn, rt, args.offset);
}
pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
- return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b11, rn, rt, args.offset);
+ return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b11, rn, rt, args.offset);
}
pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction {
- return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b10, rn, rt, args.offset);
+ return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b10, rn, rt, args.offset);
}
// Block data transfer
@@ -1234,10 +1259,9 @@ pub const Instruction = union(enum) {
} else if (args.len == 1) {
const reg = args[0];
return ldr(cond, reg, .sp, .{
- .pre_index = false,
+ .mode = .post_index,
.positive = true,
.offset = Offset.imm(4),
- .write_back = false,
});
} else {
var register_list: u16 = 0;
@@ -1259,10 +1283,9 @@ pub const Instruction = union(enum) {
} else if (args.len == 1) {
const reg = args[0];
return str(cond, reg, .sp, .{
- .pre_index = true,
+ .mode = .pre_index,
.positive = false,
.offset = Offset.imm(4),
- .write_back = true,
});
} else {
var register_list: u16 = 0;
@@ -1447,10 +1470,9 @@ test "aliases" {
.{ // pop { r6 }
.actual = Instruction.pop(.al, .{.r6}),
.expected = Instruction.ldr(.al, .r6, .sp, .{
- .pre_index = false,
+ .mode = .post_index,
.positive = true,
.offset = Instruction.Offset.imm(4),
- .write_back = false,
}),
},
.{ // pop { r1, r5 }
@@ -1460,10 +1482,9 @@ test "aliases" {
.{ // push { r3 }
.actual = Instruction.push(.al, .{.r3}),
.expected = Instruction.str(.al, .r3, .sp, .{
- .pre_index = true,
+ .mode = .pre_index,
.positive = false,
.offset = Instruction.Offset.imm(4),
- .write_back = true,
}),
},
.{ // push { r0, r2 }
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index c0642a59de..e349f0186e 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -3892,7 +3892,7 @@ pub const FuncGen = struct {
return self.builder.buildBitCast(operand, llvm_dest_ty.pointerType(0), "");
}
- if (operand_ty.zigTypeTag() == .Int and inst_ty.zigTypeTag() == .Pointer) {
+ if (operand_ty.zigTypeTag() == .Int and inst_ty.isPtrAtRuntime()) {
return self.builder.buildIntToPtr(operand, llvm_dest_ty, "");
}
diff --git a/src/type.zig b/src/type.zig
index 68085500bc..a84a0f4520 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -2602,7 +2602,7 @@ pub const Type = extern union {
const payload = self.castTag(.pointer).?.data;
return payload.@"allowzero";
},
- else => false,
+ else => return self.zigTypeTag() == .Optional,
};
}