authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-08-27 17:03:42+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-28 12:32:02-07:00
log8d036d1d78bd6db5fd39b30c6182196c1e49a3db
treeae65a34daabcc80a7b1faf04918fd8fb9ead2565
parentc6024691cfc3fa329cf1b7ac9fcb0855b7211aa0

Sema: allow cast builtins on vectors

The following cast builtins did not previously work on vectors, and have been made to: * `@floatCast` * `@ptrFromInt` * `@intFromPtr` * `@floatFromInt` * `@intFromFloat` * `@intFromBool` Resolves: #16267

4 files changed, 416 insertions(+), 76 deletions(-)

src/AstGen.zig+1-1
...@@ -8041,7 +8041,7 @@ fn builtinCall(...@@ -8041,7 +8041,7 @@ fn builtinCall(
8041 .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0], .compile_error),8041 .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0], .compile_error),
8042 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),8042 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),
8043 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),8043 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),
8044 .int_from_bool => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .int_from_bool),8044 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool),
8045 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0], .embed_file),8045 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[0], .embed_file),
8046 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name),8046 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name),
8047 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety),8047 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety),
src/Sema.zig+285-73
...@@ -9750,19 +9750,52 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -9750,19 +9750,52 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
9750 const mod = sema.mod;9750 const mod = sema.mod;
9751 const inst_data = sema.code.instructions.items(.data)[inst].un_node;9751 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
9752 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };9752 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
9753 const ptr = try sema.resolveInst(inst_data.operand);9753 const operand = try sema.resolveInst(inst_data.operand);
9754 const ptr_ty = sema.typeOf(ptr);9754 const operand_ty = sema.typeOf(operand);
9755 const ptr_ty = operand_ty.scalarType(mod);
9756 const is_vector = operand_ty.zigTypeTag(mod) == .Vector;
9755 if (!ptr_ty.isPtrAtRuntime(mod)) {9757 if (!ptr_ty.isPtrAtRuntime(mod)) {
9756 return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(mod)});9758 return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(mod)});
9757 }9759 }
9758 if (try sema.resolveMaybeUndefValIntable(ptr)) |ptr_val| {9760 if (try sema.resolveMaybeUndefValIntable(operand)) |operand_val| ct: {
9759 return Air.internedToRef((try mod.intValue(9761 if (!is_vector) {
9760 Type.usize,9762 return Air.internedToRef((try mod.intValue(
9761 (try ptr_val.getUnsignedIntAdvanced(mod, sema)).?,9763 Type.usize,
9762 )).toIntern());9764 (try operand_val.getUnsignedIntAdvanced(mod, sema)).?,
9765 )).toIntern());
9766 }
9767 const len = operand_ty.vectorLen(mod);
9768 const dest_ty = try mod.vectorType(.{ .child = .usize_type, .len = len });
9769 const new_elems = try sema.arena.alloc(InternPool.Index, len);
9770 for (new_elems, 0..) |*new_elem, i| {
9771 const ptr_val = try operand_val.elemValue(mod, i);
9772 const addr = try ptr_val.getUnsignedIntAdvanced(mod, sema) orelse {
9773 // A vector element wasn't an integer pointer. This is a runtime operation.
9774 break :ct;
9775 };
9776 new_elem.* = (try mod.intValue(
9777 Type.usize,
9778 addr,
9779 )).toIntern();
9780 }
9781 return Air.internedToRef(try mod.intern(.{ .aggregate = .{
9782 .ty = dest_ty.toIntern(),
9783 .storage = .{ .elems = new_elems },
9784 } }));
9763 }9785 }
9764 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);9786 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
9765 return block.addUnOp(.int_from_ptr, ptr);9787 if (!is_vector) {
9788 return block.addUnOp(.int_from_ptr, operand);
9789 }
9790 const len = operand_ty.vectorLen(mod);
9791 const dest_ty = try mod.vectorType(.{ .child = .usize_type, .len = len });
9792 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
9793 for (new_elems, 0..) |*new_elem, i| {
9794 const idx_ref = try mod.intRef(Type.usize, i);
9795 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
9796 new_elem.* = try block.addUnOp(.int_from_ptr, old_elem);
9797 }
9798 return block.addAggregateInit(dest_ty, new_elems);
9766}9799}
97679800
9768fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9801fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -10133,44 +10166,73 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -10133,44 +10166,73 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
10133 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;10166 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
1013410167
10135 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast");10168 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast");
10169 const dest_scalar_ty = dest_ty.scalarType(mod);
10170
10136 const operand = try sema.resolveInst(extra.rhs);10171 const operand = try sema.resolveInst(extra.rhs);
10172 const operand_ty = sema.typeOf(operand);
10173 const operand_scalar_ty = operand_ty.scalarType(mod);
10174
10175 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
10176 const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
1013710177
10138 const target = mod.getTarget();10178 const target = mod.getTarget();
10139 const dest_is_comptime_float = switch (dest_ty.zigTypeTag(mod)) {10179 const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(mod)) {
10140 .ComptimeFloat => true,10180 .ComptimeFloat => true,
10141 .Float => false,10181 .Float => false,
10142 else => return sema.fail(10182 else => return sema.fail(
10143 block,10183 block,
10144 src,10184 src,
10145 "expected float type, found '{}'",10185 "expected float or vector type, found '{}'",
10146 .{dest_ty.fmt(mod)},10186 .{dest_ty.fmt(mod)},
10147 ),10187 ),
10148 };10188 };
1014910189
10150 const operand_ty = sema.typeOf(operand);10190 switch (operand_scalar_ty.zigTypeTag(mod)) {
10151 switch (operand_ty.zigTypeTag(mod)) {
10152 .ComptimeFloat, .Float, .ComptimeInt => {},10191 .ComptimeFloat, .Float, .ComptimeInt => {},
10153 else => return sema.fail(10192 else => return sema.fail(
10154 block,10193 block,
10155 operand_src,10194 operand_src,
10156 "expected float type, found '{}'",10195 "expected float or vector type, found '{}'",
10157 .{operand_ty.fmt(mod)},10196 .{operand_ty.fmt(mod)},
10158 ),10197 ),
10159 }10198 }
1016010199
10161 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {10200 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
10162 return Air.internedToRef((try operand_val.floatCast(dest_ty, mod)).toIntern());10201 if (!is_vector) {
10202 return Air.internedToRef((try operand_val.floatCast(dest_ty, mod)).toIntern());
10203 }
10204 const vec_len = operand_ty.vectorLen(mod);
10205 const new_elems = try sema.arena.alloc(InternPool.Index, vec_len);
10206 for (new_elems, 0..) |*new_elem, i| {
10207 const old_elem = try operand_val.elemValue(mod, i);
10208 new_elem.* = (try old_elem.floatCast(dest_scalar_ty, mod)).toIntern();
10209 }
10210 return Air.internedToRef(try mod.intern(.{ .aggregate = .{
10211 .ty = dest_ty.toIntern(),
10212 .storage = .{ .elems = new_elems },
10213 } }));
10163 }10214 }
10164 if (dest_is_comptime_float) {10215 if (dest_is_comptime_float) {
10165 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});10216 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
10166 }10217 }
10167 const src_bits = operand_ty.floatBits(target);10218 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
10168 const dst_bits = dest_ty.floatBits(target);10219
10220 const src_bits = operand_scalar_ty.floatBits(target);
10221 const dst_bits = dest_scalar_ty.floatBits(target);
10169 if (dst_bits >= src_bits) {10222 if (dst_bits >= src_bits) {
10170 return sema.coerce(block, dest_ty, operand, operand_src);10223 return sema.coerce(block, dest_ty, operand, operand_src);
10171 }10224 }
10172 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);10225 if (!is_vector) {
10173 return block.addTyOp(.fptrunc, dest_ty, operand);10226 return block.addTyOp(.fptrunc, dest_ty, operand);
10227 }
10228 const vec_len = operand_ty.vectorLen(mod);
10229 const new_elems = try sema.arena.alloc(Air.Inst.Ref, vec_len);
10230 for (new_elems, 0..) |*new_elem, i| {
10231 const idx_ref = try mod.intRef(Type.usize, i);
10232 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
10233 new_elem.* = try block.addTyOp(.fptrunc, dest_scalar_ty, old_elem);
10234 }
10235 return block.addAggregateInit(dest_ty, new_elems);
10174}10236}
1017510237
10176fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {10238fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -19761,13 +19823,51 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19761,13 +19823,51 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19761fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {19823fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19762 const mod = sema.mod;19824 const mod = sema.mod;
19763 const inst_data = sema.code.instructions.items(.data)[inst].un_node;19825 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
19826 const src = inst_data.src();
19764 const operand = try sema.resolveInst(inst_data.operand);19827 const operand = try sema.resolveInst(inst_data.operand);
19828 const operand_ty = sema.typeOf(operand);
19829 const is_vector = operand_ty.zigTypeTag(mod) == .Vector;
19830 const operand_scalar_ty = operand_ty.scalarType(mod);
19831 if (operand_scalar_ty.toIntern() != .bool_type) {
19832 return sema.fail(block, src, "expected 'bool', found '{}'", .{operand_scalar_ty.zigTypeTag(mod)});
19833 }
19765 if (try sema.resolveMaybeUndefVal(operand)) |val| {19834 if (try sema.resolveMaybeUndefVal(operand)) |val| {
19766 if (val.isUndef(mod)) return mod.undefRef(Type.u1);19835 if (!is_vector) {
19767 if (val.toBool()) return Air.internedToRef((try mod.intValue(Type.u1, 1)).toIntern());19836 if (val.isUndef(mod)) return mod.undefRef(Type.u1);
19768 return Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern());19837 if (val.toBool()) return Air.internedToRef((try mod.intValue(Type.u1, 1)).toIntern());
19838 return Air.internedToRef((try mod.intValue(Type.u1, 0)).toIntern());
19839 }
19840 const len = operand_ty.vectorLen(mod);
19841 const dest_ty = try mod.vectorType(.{ .child = .u1_type, .len = len });
19842 if (val.isUndef(mod)) return mod.undefRef(dest_ty);
19843 const new_elems = try sema.arena.alloc(InternPool.Index, len);
19844 for (new_elems, 0..) |*new_elem, i| {
19845 const old_elem = try val.elemValue(mod, i);
19846 const new_val = if (old_elem.isUndef(mod))
19847 try mod.undefValue(Type.u1)
19848 else if (old_elem.toBool())
19849 try mod.intValue(Type.u1, 1)
19850 else
19851 try mod.intValue(Type.u1, 0);
19852 new_elem.* = new_val.toIntern();
19853 }
19854 return Air.internedToRef(try mod.intern(.{ .aggregate = .{
19855 .ty = dest_ty.toIntern(),
19856 .storage = .{ .elems = new_elems },
19857 } }));
19858 }
19859 if (!is_vector) {
19860 return block.addUnOp(.int_from_bool, operand);
19769 }19861 }
19770 return block.addUnOp(.int_from_bool, operand);19862 const len = operand_ty.vectorLen(mod);
19863 const dest_ty = try mod.vectorType(.{ .child = .u1_type, .len = len });
19864 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
19865 for (new_elems, 0..) |*new_elem, i| {
19866 const idx_ref = try mod.intRef(Type.usize, i);
19867 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
19868 new_elem.* = try block.addUnOp(.int_from_bool, old_elem);
19869 }
19870 return block.addAggregateInit(dest_ty, new_elems);
19771}19871}
1977219872
19773fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {19873fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -21050,34 +21150,74 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro...@@ -21050,34 +21150,74 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
21050 const operand = try sema.resolveInst(extra.rhs);21150 const operand = try sema.resolveInst(extra.rhs);
21051 const operand_ty = sema.typeOf(operand);21151 const operand_ty = sema.typeOf(operand);
2105221152
21053 _ = try sema.checkIntType(block, src, dest_ty);21153 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
21054 try sema.checkFloatType(block, operand_src, operand_ty);21154 const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
2105521155
21056 if (try sema.resolveMaybeUndefVal(operand)) |val| {21156 const dest_scalar_ty = dest_ty.scalarType(mod);
21057 const result_val = try sema.intFromFloat(block, operand_src, val, operand_ty, dest_ty);21157 const operand_scalar_ty = operand_ty.scalarType(mod);
21158
21159 _ = try sema.checkIntType(block, src, dest_scalar_ty);
21160 try sema.checkFloatType(block, operand_src, operand_scalar_ty);
21161
21162 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
21163 const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty);
21058 return Air.internedToRef(result_val.toIntern());21164 return Air.internedToRef(result_val.toIntern());
21059 } else if (dest_ty.zigTypeTag(mod) == .ComptimeInt) {21165 } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeInt) {
21060 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime-known");21166 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime-known");
21061 }21167 }
2106221168
21063 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);21169 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);
21064 if (dest_ty.intInfo(mod).bits == 0) {21170 if (dest_scalar_ty.intInfo(mod).bits == 0) {
21171 if (!is_vector) {
21172 if (block.wantSafety()) {
21173 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, Air.internedToRef((try mod.floatValue(operand_ty, 0.0)).toIntern()));
21174 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
21175 }
21176 return Air.internedToRef((try mod.intValue(dest_ty, 0)).toIntern());
21177 }
21178 if (block.wantSafety()) {
21179 const len = dest_ty.vectorLen(mod);
21180 for (0..len) |i| {
21181 const idx_ref = try mod.intRef(Type.usize, i);
21182 const elem_ref = try block.addBinOp(.array_elem_val, operand, idx_ref);
21183 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, elem_ref, Air.internedToRef((try mod.floatValue(operand_scalar_ty, 0.0)).toIntern()));
21184 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
21185 }
21186 }
21187 return Air.internedToRef(try mod.intern(.{ .aggregate = .{
21188 .ty = dest_ty.toIntern(),
21189 .storage = .{ .repeated_elem = (try mod.intValue(dest_scalar_ty, 0)).toIntern() },
21190 } }));
21191 }
21192 if (!is_vector) {
21193 const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand);
21065 if (block.wantSafety()) {21194 if (block.wantSafety()) {
21066 const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, Air.internedToRef((try mod.floatValue(operand_ty, 0.0)).toIntern()));21195 const back = try block.addTyOp(.float_from_int, operand_ty, result);
21196 const diff = try block.addBinOp(.sub, operand, back);
21197 const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, Air.internedToRef((try mod.floatValue(operand_ty, 1.0)).toIntern()));
21198 const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, Air.internedToRef((try mod.floatValue(operand_ty, -1.0)).toIntern()));
21199 const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);
21067 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);21200 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
21068 }21201 }
21069 return Air.internedToRef((try mod.intValue(dest_ty, 0)).toIntern());21202 return result;
21070 }21203 }
21071 const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_ty, operand);21204 const len = dest_ty.vectorLen(mod);
21072 if (block.wantSafety()) {21205 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
21073 const back = try block.addTyOp(.float_from_int, operand_ty, result);21206 for (new_elems, 0..) |*new_elem, i| {
21074 const diff = try block.addBinOp(.sub, operand, back);21207 const idx_ref = try mod.intRef(Type.usize, i);
21075 const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, Air.internedToRef((try mod.floatValue(operand_ty, 1.0)).toIntern()));21208 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
21076 const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, Air.internedToRef((try mod.floatValue(operand_ty, -1.0)).toIntern()));21209 const result = try block.addTyOp(if (block.float_mode == .Optimized) .int_from_float_optimized else .int_from_float, dest_scalar_ty, old_elem);
21077 const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);21210 if (block.wantSafety()) {
21078 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);21211 const back = try block.addTyOp(.float_from_int, operand_scalar_ty, result);
21212 const diff = try block.addBinOp(.sub, old_elem, back);
21213 const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, Air.internedToRef((try mod.floatValue(operand_scalar_ty, 1.0)).toIntern()));
21214 const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, Air.internedToRef((try mod.floatValue(operand_scalar_ty, -1.0)).toIntern()));
21215 const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);
21216 try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
21217 }
21218 new_elem.* = result;
21079 }21219 }
21080 return result;21220 return block.addAggregateInit(dest_ty, new_elems);
21081}21221}
2108221222
21083fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {21223fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -21090,18 +21230,34 @@ fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro...@@ -21090,18 +21230,34 @@ fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
21090 const operand = try sema.resolveInst(extra.rhs);21230 const operand = try sema.resolveInst(extra.rhs);
21091 const operand_ty = sema.typeOf(operand);21231 const operand_ty = sema.typeOf(operand);
2109221232
21093 try sema.checkFloatType(block, src, dest_ty);21233 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
21094 _ = try sema.checkIntType(block, operand_src, operand_ty);21234 const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
21235
21236 const dest_scalar_ty = dest_ty.scalarType(mod);
21237 const operand_scalar_ty = operand_ty.scalarType(mod);
2109521238
21096 if (try sema.resolveMaybeUndefVal(operand)) |val| {21239 try sema.checkFloatType(block, src, dest_scalar_ty);
21097 const result_val = try val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, sema.mod, sema);21240 _ = try sema.checkIntType(block, operand_src, operand_scalar_ty);
21241
21242 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
21243 const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, mod, sema);
21098 return Air.internedToRef(result_val.toIntern());21244 return Air.internedToRef(result_val.toIntern());
21099 } else if (dest_ty.zigTypeTag(mod) == .ComptimeFloat) {21245 } else if (dest_scalar_ty.zigTypeTag(mod) == .ComptimeFloat) {
21100 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime-known");21246 return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime-known");
21101 }21247 }
2110221248
21103 try sema.requireRuntimeBlock(block, inst_data.src(), operand_src);21249 try sema.requireRuntimeBlock(block, src, operand_src);
21104 return block.addTyOp(.float_from_int, dest_ty, operand);21250 if (!is_vector) {
21251 return block.addTyOp(.float_from_int, dest_ty, operand);
21252 }
21253 const len = operand_ty.vectorLen(mod);
21254 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
21255 for (new_elems, 0..) |*new_elem, i| {
21256 const idx_ref = try mod.intRef(Type.usize, i);
21257 const old_elem = try block.addBinOp(.array_elem_val, operand, idx_ref);
21258 new_elem.* = try block.addTyOp(.float_from_int, dest_scalar_ty, old_elem);
21259 }
21260 return block.addAggregateInit(dest_ty, new_elems);
21105}21261}
2110621262
21107fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {21263fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -21113,9 +21269,20 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -21113,9 +21269,20 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
2111321269
21114 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };21270 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
21115 const operand_res = try sema.resolveInst(extra.rhs);21271 const operand_res = try sema.resolveInst(extra.rhs);
21116 const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src);
2111721272
21118 const ptr_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");21273 const uncoerced_operand_ty = sema.typeOf(operand_res);
21274 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");
21275 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src);
21276
21277 const is_vector = dest_ty.zigTypeTag(mod) == .Vector;
21278 const operand_ty = if (is_vector) operand_ty: {
21279 const len = dest_ty.vectorLen(mod);
21280 break :operand_ty try mod.vectorType(.{ .child = .usize_type, .len = len });
21281 } else Type.usize;
21282
21283 const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src);
21284
21285 const ptr_ty = dest_ty.scalarType(mod);
21119 try sema.checkPtrType(block, src, ptr_ty);21286 try sema.checkPtrType(block, src, ptr_ty);
21120 const elem_ty = ptr_ty.elemType2(mod);21287 const elem_ty = ptr_ty.elemType2(mod);
21121 const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema);21288 const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema);
...@@ -21131,38 +21298,83 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -21131,38 +21298,83 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
21131 }21298 }
2113221299
21133 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {21300 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
21134 const addr = val.toUnsignedInt(mod);21301 if (!is_vector) {
21135 if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0)21302 const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align);
21136 return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)});21303 return Air.internedToRef(ptr_val.toIntern());
21137 if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0)21304 }
21138 return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});21305 const len = dest_ty.vectorLen(mod);
2113921306 const new_elems = try sema.arena.alloc(InternPool.Index, len);
21140 const ptr_val = switch (ptr_ty.zigTypeTag(mod)) {21307 for (new_elems, 0..) |*new_elem, i| {
21141 .Optional => (try mod.intern(.{ .opt = .{21308 const elem = try val.elemValue(mod, i);
21142 .ty = ptr_ty.toIntern(),21309 const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align);
21143 .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(),21310 new_elem.* = ptr_val.toIntern();
21144 } })).toValue(),21311 }
21145 .Pointer => try mod.ptrIntValue(ptr_ty, addr),21312 return Air.internedToRef(try mod.intern(.{ .aggregate = .{
21146 else => unreachable,21313 .ty = dest_ty.toIntern(),
21147 };21314 .storage = .{ .elems = new_elems },
21148 return Air.internedToRef(ptr_val.toIntern());21315 } }));
21149 }21316 }
2115021317
21151 try sema.requireRuntimeBlock(block, src, operand_src);21318 try sema.requireRuntimeBlock(block, src, operand_src);
21152 if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {21319 if (!is_vector) {
21153 if (!ptr_ty.isAllowzeroPtr(mod)) {21320 if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {
21154 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);21321 if (!ptr_ty.isAllowzeroPtr(mod)) {
21155 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);21322 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
21323 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
21324 }
21325 if (ptr_align > 1) {
21326 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, ptr_align - 1)).toIntern());
21327 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
21328 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
21329 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
21330 }
21156 }21331 }
21332 return block.addBitCast(dest_ty, operand_coerced);
21333 }
2115721334
21158 if (ptr_align > 1) {21335 const len = dest_ty.vectorLen(mod);
21159 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, ptr_align - 1)).toIntern());21336 if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {
21160 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);21337 for (0..len) |i| {
21161 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);21338 const idx_ref = try mod.intRef(Type.usize, i);
21162 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);21339 const elem_coerced = try block.addBinOp(.array_elem_val, operand_coerced, idx_ref);
21340 if (!ptr_ty.isAllowzeroPtr(mod)) {
21341 const is_non_zero = try block.addBinOp(.cmp_neq, elem_coerced, .zero_usize);
21342 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
21343 }
21344 if (ptr_align > 1) {
21345 const align_minus_1 = Air.internedToRef((try mod.intValue(Type.usize, ptr_align - 1)).toIntern());
21346 const remainder = try block.addBinOp(.bit_and, elem_coerced, align_minus_1);
21347 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
21348 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
21349 }
21163 }21350 }
21164 }21351 }
21165 return block.addBitCast(ptr_ty, operand_coerced);21352
21353 const new_elems = try sema.arena.alloc(Air.Inst.Ref, len);
21354 for (new_elems, 0..) |*new_elem, i| {
21355 const idx_ref = try mod.intRef(Type.usize, i);
21356 const old_elem = try block.addBinOp(.array_elem_val, operand_coerced, idx_ref);
21357 new_elem.* = try block.addBitCast(ptr_ty, old_elem);
21358 }
21359 return block.addAggregateInit(dest_ty, new_elems);
21360}
21361
21362fn ptrFromIntVal(sema: *Sema, block: *Block, operand_src: LazySrcLoc, operand_val: Value, ptr_ty: Type, ptr_align: u32) !Value {
21363 const mod = sema.mod;
21364 const addr = operand_val.toUnsignedInt(mod);
21365 if (!ptr_ty.isAllowzeroPtr(mod) and addr == 0)
21366 return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{ptr_ty.fmt(sema.mod)});
21367 if (addr != 0 and ptr_align != 0 and addr % ptr_align != 0)
21368 return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{ptr_ty.fmt(sema.mod)});
21369
21370 return switch (ptr_ty.zigTypeTag(mod)) {
21371 .Optional => (try mod.intern(.{ .opt = .{
21372 .ty = ptr_ty.toIntern(),
21373 .val = if (addr == 0) .none else (try mod.ptrIntValue(ptr_ty.childType(mod), addr)).toIntern(),
21374 } })).toValue(),
21375 .Pointer => try mod.ptrIntValue(ptr_ty, addr),
21376 else => unreachable,
21377 };
21166}21378}
2116721379
21168fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {21380fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
test/behavior/cast.zig+129-1
...@@ -609,7 +609,7 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {...@@ -609,7 +609,7 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
609 try expect(mem.eql(u8, std.mem.sliceTo(@as([*:0]const u8, @ptrCast(x[0].?)), 0), "window name"));609 try expect(mem.eql(u8, std.mem.sliceTo(@as([*:0]const u8, @ptrCast(x[0].?)), 0), "window name"));
610}610}
611611
612test "vector casts" {612test "@intCast on vector" {
613 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO613 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
614 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO614 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
615 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO615 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -2366,3 +2366,131 @@ test "cast builtins can wrap result in error union and optional" {...@@ -2366,3 +2366,131 @@ test "cast builtins can wrap result in error union and optional" {
2366 try S.doTheTest();2366 try S.doTheTest();
2367 try comptime S.doTheTest();2367 try comptime S.doTheTest();
2368}2368}
2369
2370test "@floatCast on vector" {
2371 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2372 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2373 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2374 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2375 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2376 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2377
2378 const S = struct {
2379 fn doTheTest() !void {
2380 var a: @Vector(3, f64) = .{ 1.5, 2.5, 3.5 };
2381 const b: @Vector(3, f32) = @floatCast(a);
2382 try expectEqual(@Vector(3, f32){ 1.5, 2.5, 3.5 }, b);
2383 }
2384 };
2385
2386 try S.doTheTest();
2387 try comptime S.doTheTest();
2388}
2389
2390test "@ptrFromInt on vector" {
2391 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2392 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2393 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2394 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2395 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2396 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2397
2398 const S = struct {
2399 fn doTheTest() !void {
2400 var a: @Vector(3, usize) = .{ 0x1000, 0x2000, 0x3000 };
2401 const b: @Vector(3, *anyopaque) = @ptrFromInt(a);
2402 try expectEqual(@Vector(3, *anyopaque){
2403 @ptrFromInt(0x1000),
2404 @ptrFromInt(0x2000),
2405 @ptrFromInt(0x3000),
2406 }, b);
2407 }
2408 };
2409
2410 try S.doTheTest();
2411 try comptime S.doTheTest();
2412}
2413
2414test "@intFromPtr on vector" {
2415 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2416 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2417 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2418 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2419 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2420 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2421
2422 const S = struct {
2423 fn doTheTest() !void {
2424 var a: @Vector(3, *anyopaque) = .{
2425 @ptrFromInt(0x1000),
2426 @ptrFromInt(0x2000),
2427 @ptrFromInt(0x3000),
2428 };
2429 const b: @Vector(3, usize) = @intFromPtr(a);
2430 try expectEqual(@Vector(3, usize){ 0x1000, 0x2000, 0x3000 }, b);
2431 }
2432 };
2433
2434 try S.doTheTest();
2435 try comptime S.doTheTest();
2436}
2437
2438test "@floatFromInt on vector" {
2439 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2440 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2441 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2442 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2443 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2444 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2445
2446 const S = struct {
2447 fn doTheTest() !void {
2448 var a: @Vector(3, u32) = .{ 10, 20, 30 };
2449 const b: @Vector(3, f32) = @floatFromInt(a);
2450 try expectEqual(@Vector(3, f32){ 10.0, 20.0, 30.0 }, b);
2451 }
2452 };
2453
2454 try S.doTheTest();
2455 try comptime S.doTheTest();
2456}
2457
2458test "@intFromFloat on vector" {
2459 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2460 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2461 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2462 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2463 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2464 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2465
2466 const S = struct {
2467 fn doTheTest() !void {
2468 var a: @Vector(3, f32) = .{ 10.3, 20.5, 30.7 };
2469 const b: @Vector(3, u32) = @intFromFloat(a);
2470 try expectEqual(@Vector(3, u32){ 10, 20, 30 }, b);
2471 }
2472 };
2473
2474 try S.doTheTest();
2475 try comptime S.doTheTest();
2476}
2477
2478test "@intFromBool on vector" {
2479 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
2480 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2481 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2482 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2483 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2484 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2485
2486 const S = struct {
2487 fn doTheTest() !void {
2488 var a: @Vector(3, bool) = .{ false, true, false };
2489 const b: @Vector(3, u1) = @intFromBool(a);
2490 try expectEqual(@Vector(3, u1){ 0, 1, 0 }, b);
2491 }
2492 };
2493
2494 try S.doTheTest();
2495 try comptime S.doTheTest();
2496}
test/cases/compile_errors/invalid_float_casts.zig+1-1
...@@ -22,4 +22,4 @@ export fn qux() void {...@@ -22,4 +22,4 @@ export fn qux() void {
22// :3:40: error: unable to cast runtime value to 'comptime_float'22// :3:40: error: unable to cast runtime value to 'comptime_float'
23// :7:18: error: expected integer type, found 'f32'23// :7:18: error: expected integer type, found 'f32'
24// :11:32: error: expected integer type, found 'f32'24// :11:32: error: expected integer type, found 'f32'
25// :15:29: error: expected float type, found 'u32'25// :15:29: error: expected float or vector type, found 'u32'