authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-22 13:28:57-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-22 13:28:57-05:00
log52bb71867d8d6e738eefb7dafead875eb21a4ae7
tree317c8b0f23e603c590f943700587c3a6596bc8ff
parent2fe8a0831f2830a3357cdf65b2ebfe1214b13a83
signaturelock-open Commit is signed but in an unrecognized format.

implement vector negation

also fix vector behavior tests, they weren't actually testing runtime vectors, but now they are. See #903

5 files changed, 157 insertions(+), 47 deletions(-)

src/codegen.cpp+6-5
...@@ -3229,7 +3229,8 @@ static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstruc...@@ -3229,7 +3229,8 @@ static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstruc
3229static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) {3229static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) {
3230 IrUnOp op_id = un_op_instruction->op_id;3230 IrUnOp op_id = un_op_instruction->op_id;
3231 LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value);3231 LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value);
3232 ZigType *expr_type = un_op_instruction->value->value.type;3232 ZigType *operand_type = un_op_instruction->value->value.type;
3233 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
32333234
3234 switch (op_id) {3235 switch (op_id) {
3235 case IrUnOpInvalid:3236 case IrUnOpInvalid:
...@@ -3239,16 +3240,16 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst...@@ -3239,16 +3240,16 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
3239 case IrUnOpNegation:3240 case IrUnOpNegation:
3240 case IrUnOpNegationWrap:3241 case IrUnOpNegationWrap:
3241 {3242 {
3242 if (expr_type->id == ZigTypeIdFloat) {3243 if (scalar_type->id == ZigTypeIdFloat) {
3243 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &un_op_instruction->base));3244 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &un_op_instruction->base));
3244 return LLVMBuildFNeg(g->builder, expr, "");3245 return LLVMBuildFNeg(g->builder, expr, "");
3245 } else if (expr_type->id == ZigTypeIdInt) {3246 } else if (scalar_type->id == ZigTypeIdInt) {
3246 if (op_id == IrUnOpNegationWrap) {3247 if (op_id == IrUnOpNegationWrap) {
3247 return LLVMBuildNeg(g->builder, expr, "");3248 return LLVMBuildNeg(g->builder, expr, "");
3248 } else if (ir_want_runtime_safety(g, &un_op_instruction->base)) {3249 } else if (ir_want_runtime_safety(g, &un_op_instruction->base)) {
3249 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));3250 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
3250 return gen_overflow_op(g, expr_type, AddSubMulSub, zero, expr);3251 return gen_overflow_op(g, operand_type, AddSubMulSub, zero, expr);
3251 } else if (expr_type->data.integral.is_signed) {3252 } else if (scalar_type->data.integral.is_signed) {
3252 return LLVMBuildNSWNeg(g->builder, expr, "");3253 return LLVMBuildNSWNeg(g->builder, expr, "");
3253 } else {3254 } else {
3254 return LLVMBuildNUWNeg(g->builder, expr, "");3255 return LLVMBuildNUWNeg(g->builder, expr, "");
src/ir.cpp+71-33
...@@ -14620,6 +14620,41 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_...@@ -14620,6 +14620,41 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_
14620 zig_unreachable();14620 zig_unreachable();
14621}14621}
1462214622
14623static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *scalar_type,
14624 ConstExprValue *operand_val, ConstExprValue *scalar_out_val, bool is_wrap_op)
14625{
14626 bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat);
14627
14628 bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) ||
14629 scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op));
14630
14631 if (!ok_type) {
14632 const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
14633 return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name)));
14634 }
14635
14636 if (is_float) {
14637 float_negate(scalar_out_val, operand_val);
14638 } else if (is_wrap_op) {
14639 bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint,
14640 scalar_type->data.integral.bit_count);
14641 } else {
14642 bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint);
14643 }
14644
14645 scalar_out_val->type = scalar_type;
14646 scalar_out_val->special = ConstValSpecialStatic;
14647
14648 if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) {
14649 return nullptr;
14650 }
14651
14652 if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) {
14653 return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow"));
14654 }
14655 return nullptr;
14656}
14657
14623static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) {14658static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) {
14624 IrInstruction *value = instruction->value->child;14659 IrInstruction *value = instruction->value->child;
14625 ZigType *expr_type = value->value.type;14660 ZigType *expr_type = value->value.type;
...@@ -14628,47 +14663,50 @@ static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *ins...@@ -14628,47 +14663,50 @@ static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *ins
1462814663
14629 bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);14664 bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
1463014665
14631 bool is_float = (expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat);14666 ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
1463214667
14633 if ((expr_type->id == ZigTypeIdInt && expr_type->data.integral.is_signed) ||14668 if (instr_is_comptime(value)) {
14634 expr_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op))14669 ConstExprValue *operand_val = ir_resolve_const(ira, value, UndefBad);
14635 {14670 if (!operand_val)
14636 if (instr_is_comptime(value)) {14671 return ira->codegen->invalid_instruction;
14637 ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
14638 if (!target_const_val)
14639 return ira->codegen->invalid_instruction;
1464014672
14641 IrInstruction *result = ir_const(ira, &instruction->base, expr_type);14673 IrInstruction *result_instruction = ir_const(ira, &instruction->base, expr_type);
14642 ConstExprValue *out_val = &result->value;14674 ConstExprValue *out_val = &result_instruction->value;
14643 if (is_float) {14675 if (expr_type->id == ZigTypeIdVector) {
14644 float_negate(out_val, target_const_val);14676 expand_undef_array(ira->codegen, operand_val);
14645 } else if (is_wrap_op) {14677 out_val->special = ConstValSpecialUndef;
14646 bigint_negate_wrap(&out_val->data.x_bigint, &target_const_val->data.x_bigint,14678 expand_undef_array(ira->codegen, out_val);
14647 expr_type->data.integral.bit_count);14679 size_t len = expr_type->data.vector.len;
14648 } else {14680 for (size_t i = 0; i < len; i += 1) {
14649 bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint);14681 ConstExprValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i];
14650 }14682 ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
14651 if (is_wrap_op || is_float || expr_type->id == ZigTypeIdComptimeInt) {14683 assert(scalar_operand_val->type == scalar_type);
14652 return result;14684 assert(scalar_out_val->type == scalar_type);
14685 ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base, scalar_type,
14686 scalar_operand_val, scalar_out_val, is_wrap_op);
14687 if (msg != nullptr) {
14688 add_error_note(ira->codegen, msg, instruction->base.source_node,
14689 buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
14690 return ira->codegen->invalid_instruction;
14691 }
14653 }14692 }
1465414693 out_val->type = expr_type;
14655 if (!bigint_fits_in_bits(&out_val->data.x_bigint, expr_type->data.integral.bit_count, true)) {14694 out_val->special = ConstValSpecialStatic;
14656 ir_add_error(ira, &instruction->base, buf_sprintf("negation caused overflow"));14695 } else {
14696 if (ir_eval_negation_scalar(ira, &instruction->base, scalar_type, operand_val, out_val,
14697 is_wrap_op) != nullptr)
14698 {
14657 return ira->codegen->invalid_instruction;14699 return ira->codegen->invalid_instruction;
14658 }14700 }
14659 return result;
14660 }14701 }
1466114702 return result_instruction;
14662 IrInstruction *result = ir_build_un_op(&ira->new_irb,
14663 instruction->base.scope, instruction->base.source_node,
14664 instruction->op_id, value);
14665 result->value.type = expr_type;
14666 return result;
14667 }14703 }
1466814704
14669 const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";14705 IrInstruction *result = ir_build_un_op(&ira->new_irb,
14670 ir_add_error(ira, &instruction->base, buf_sprintf(fmt, buf_ptr(&expr_type->name)));14706 instruction->base.scope, instruction->base.source_node,
14671 return ira->codegen->invalid_instruction;14707 instruction->op_id, value);
14708 result->value.type = expr_type;
14709 return result;
14672}14710}
1467314711
14674static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) {14712static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) {
test/compile_errors.zig+12
...@@ -1,6 +1,18 @@...@@ -1,6 +1,18 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.addTest(
5 "comptime vector overflow shows the index",
6 \\comptime {
7 \\ var a: @Vector(4, u8) = []u8{ 1, 2, 255, 4 };
8 \\ var b: @Vector(4, u8) = []u8{ 5, 6, 1, 8 };
9 \\ var x = a + b;
10 \\}
11 ,
12 ".tmp_source.zig:4:15: error: operation caused overflow",
13 ".tmp_source.zig:4:15: note: when computing vector element at index 2",
14 );
15
4 cases.addTest(16 cases.addTest(
5 "packed struct with fields of not allowed types",17 "packed struct with fields of not allowed types",
6 \\const A = packed struct {18 \\const A = packed struct {
test/runtime_safety.zig+41
...@@ -118,6 +118,47 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -118,6 +118,47 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
118 \\}118 \\}
119 );119 );
120120
121 cases.addRuntimeSafety("vector integer subtraction overflow",
122 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
123 \\ @import("std").os.exit(126);
124 \\}
125 \\pub fn main() void {
126 \\ var a: @Vector(4, u32) = []u32{ 1, 2, 8, 4 };
127 \\ var b: @Vector(4, u32) = []u32{ 5, 6, 7, 8 };
128 \\ const x = sub(b, a);
129 \\}
130 \\fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) {
131 \\ return a - b;
132 \\}
133 );
134
135 cases.addRuntimeSafety("vector integer multiplication overflow",
136 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
137 \\ @import("std").os.exit(126);
138 \\}
139 \\pub fn main() void {
140 \\ var a: @Vector(4, u8) = []u8{ 1, 2, 200, 4 };
141 \\ var b: @Vector(4, u8) = []u8{ 5, 6, 2, 8 };
142 \\ const x = mul(b, a);
143 \\}
144 \\fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) {
145 \\ return a * b;
146 \\}
147 );
148
149 cases.addRuntimeSafety("vector integer negation overflow",
150 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
151 \\ @import("std").os.exit(126);
152 \\}
153 \\pub fn main() void {
154 \\ var a: @Vector(4, i16) = []i16{ 1, -32768, 200, 4 };
155 \\ const x = neg(a);
156 \\}
157 \\fn neg(a: @Vector(4, i16)) @Vector(4, i16) {
158 \\ return -a;
159 \\}
160 );
161
121 cases.addRuntimeSafety("integer subtraction overflow",162 cases.addRuntimeSafety("integer subtraction overflow",
122 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {163 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
123 \\ @import("std").os.exit(126);164 \\ @import("std").os.exit(126);
test/stage1/behavior/vector.zig+27-9
...@@ -5,11 +5,28 @@ const expect = std.testing.expect;...@@ -5,11 +5,28 @@ const expect = std.testing.expect;
5test "vector wrap operators" {5test "vector wrap operators" {
6 const S = struct {6 const S = struct {
7 fn doTheTest() void {7 fn doTheTest() void {
8 const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };8 var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
9 const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };9 var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
10 expect(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));10 expect(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ -2147483648, 2147483645, 33, 44 }));
11 expect(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));11 expect(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 2147483646, 2147483647, 27, 36 }));
12 expect(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));12 expect(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 2147483647, 2, 90, 160 }));
13 var z: @Vector(4, i32) = [4]i32{ 1, 2, 3, -2147483648 };
14 expect(mem.eql(i32, ([4]i32)(-%z), [4]i32{ -1, -2, -3, -2147483648 }));
15 }
16 };
17 S.doTheTest();
18 comptime S.doTheTest();
19}
20
21test "vector int operators" {
22 const S = struct {
23 fn doTheTest() void {
24 var v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
25 var x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
26 expect(mem.eql(i32, ([4]i32)(v + x), [4]i32{ 11, 22, 33, 44 }));
27 expect(mem.eql(i32, ([4]i32)(v - x), [4]i32{ 9, 18, 27, 36 }));
28 expect(mem.eql(i32, ([4]i32)(v * x), [4]i32{ 10, 40, 90, 160 }));
29 expect(mem.eql(i32, ([4]i32)(-v), [4]i32{ -10, -20, -30, -40 }));
13 }30 }
14 };31 };
15 S.doTheTest();32 S.doTheTest();
...@@ -19,11 +36,12 @@ test "vector wrap operators" {...@@ -19,11 +36,12 @@ test "vector wrap operators" {
19test "vector float operators" {36test "vector float operators" {
20 const S = struct {37 const S = struct {
21 fn doTheTest() void {38 fn doTheTest() void {
22 const v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 };39 var v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 };
23 const x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 };40 var x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 };
24 expect(mem.eql(f32, ([4]f32)(v + x), [4]f32{ 11, 22, 33, 44 }));41 expect(mem.eql(f32, ([4]f32)(v + x), [4]f32{ 11, 22, 33, 44 }));
25 expect(mem.eql(f32, ([4]f32)(v - x), [4]f32{ 9, 18, 27, 36 }));42 expect(mem.eql(f32, ([4]f32)(v - x), [4]f32{ 9, 18, 27, 36 }));
26 expect(mem.eql(f32, ([4]f32)(v * x), [4]f32{ 10, 40, 90, 160 }));43 expect(mem.eql(f32, ([4]f32)(v * x), [4]f32{ 10, 40, 90, 160 }));
44 expect(mem.eql(f32, ([4]f32)(-x), [4]f32{ -1, -2, -3, -4 }));
27 }45 }
28 };46 };
29 S.doTheTest();47 S.doTheTest();
...@@ -33,8 +51,8 @@ test "vector float operators" {...@@ -33,8 +51,8 @@ test "vector float operators" {
33test "vector bit operators" {51test "vector bit operators" {
34 const S = struct {52 const S = struct {
35 fn doTheTest() void {53 fn doTheTest() void {
36 const v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 };54 var v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 };
37 const x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 };55 var x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 };
38 expect(mem.eql(u8, ([4]u8)(v ^ x), [4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 }));56 expect(mem.eql(u8, ([4]u8)(v ^ x), [4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 }));
39 expect(mem.eql(u8, ([4]u8)(v | x), [4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 }));57 expect(mem.eql(u8, ([4]u8)(v | x), [4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 }));
40 expect(mem.eql(u8, ([4]u8)(v & x), [4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 }));58 expect(mem.eql(u8, ([4]u8)(v & x), [4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 }));