| author | |
| committer | |
| log | 76f53960778e84ab49730edb77b85490b07fbea2 |
| tree | 9c14f9af00d722b3ebafc8462acf9a5f8e082663 |
| parent | 86209e1a9259dca40803e56d612beacf5a35855c |
| signature |
4 files changed, 85 insertions(+), 17 deletions(-)
src/all_types.hpp+1| ... | @@ -1771,6 +1771,7 @@ struct ZigLLVMFnKey { | ... | @@ -1771,6 +1771,7 @@ struct ZigLLVMFnKey { |
| 1771 | } overflow_arithmetic; | 1771 | } overflow_arithmetic; |
| 1772 | struct { | 1772 | struct { |
| 1773 | uint32_t bit_count; | 1773 | uint32_t bit_count; |
| 1774 | uint32_t vector_len; // 0 means not a vector | ||
| 1774 | } bswap; | 1775 | } bswap; |
| 1775 | struct { | 1776 | struct { |
| 1776 | uint32_t bit_count; | 1777 | uint32_t bit_count; |
src/codegen.cpp+21-7| ... | @@ -4505,7 +4505,13 @@ static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *exec | ... | @@ -4505,7 +4505,13 @@ static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *exec |
| 4505 | } | 4505 | } |
| 4506 | } | 4506 | } |
| 4507 | 4507 | ||
| 4508 | static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnId fn_id) { | 4508 | static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *expr_type, BuiltinFnId fn_id) { |
| 4509 | bool is_vector = expr_type->id == ZigTypeIdVector; | ||
| 4510 | ZigType *int_type = is_vector ? expr_type->data.vector.elem_type : expr_type; | ||
| 4511 | assert(int_type->id == ZigTypeIdInt); | ||
| 4512 | uint32_t vector_len = 0; | ||
| 4513 | if (is_vector) | ||
| 4514 | vector_len = expr_type->data.vector.len; | ||
| 4509 | ZigLLVMFnKey key = {}; | 4515 | ZigLLVMFnKey key = {}; |
| 4510 | const char *fn_name; | 4516 | const char *fn_name; |
| 4511 | uint32_t n_args; | 4517 | uint32_t n_args; |
| ... | @@ -4529,6 +4535,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnI | ... | @@ -4529,6 +4535,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnI |
| 4529 | n_args = 1; | 4535 | n_args = 1; |
| 4530 | key.id = ZigLLVMFnIdBswap; | 4536 | key.id = ZigLLVMFnIdBswap; |
| 4531 | key.data.bswap.bit_count = (uint32_t)int_type->data.integral.bit_count; | 4537 | key.data.bswap.bit_count = (uint32_t)int_type->data.integral.bit_count; |
| 4538 | key.data.bswap.vector_len = vector_len; | ||
| 4532 | } else if (fn_id == BuiltinFnIdBitReverse) { | 4539 | } else if (fn_id == BuiltinFnIdBitReverse) { |
| 4533 | fn_name = "bitreverse"; | 4540 | fn_name = "bitreverse"; |
| 4534 | n_args = 1; | 4541 | n_args = 1; |
| ... | @@ -4543,12 +4550,15 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnI | ... | @@ -4543,12 +4550,15 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnI |
| 4543 | return existing_entry->value; | 4550 | return existing_entry->value; |
| 4544 | 4551 | ||
| 4545 | char llvm_name[64]; | 4552 | char llvm_name[64]; |
| 4546 | sprintf(llvm_name, "llvm.%s.i%" PRIu32, fn_name, int_type->data.integral.bit_count); | 4553 | if (is_vector) |
| 4554 | sprintf(llvm_name, "llvm.%s.v%" PRIu32 "i%" PRIu32, fn_name, vector_len, int_type->data.integral.bit_count); | ||
| 4555 | else | ||
| 4556 | sprintf(llvm_name, "llvm.%s.i%" PRIu32, fn_name, int_type->data.integral.bit_count); | ||
| 4547 | LLVMTypeRef param_types[] = { | 4557 | LLVMTypeRef param_types[] = { |
| 4548 | get_llvm_type(g, int_type), | 4558 | get_llvm_type(g, expr_type), |
| 4549 | LLVMInt1Type(), | 4559 | LLVMInt1Type(), |
| 4550 | }; | 4560 | }; |
| 4551 | LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, int_type), param_types, n_args, false); | 4561 | LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, expr_type), param_types, n_args, false); |
| 4552 | LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type); | 4562 | LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type); |
| 4553 | assert(LLVMGetIntrinsicID(fn_val)); | 4563 | assert(LLVMGetIntrinsicID(fn_val)); |
| 4554 | 4564 | ||
| ... | @@ -5542,15 +5552,19 @@ static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrIn | ... | @@ -5542,15 +5552,19 @@ static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrIn |
| 5542 | 5552 | ||
| 5543 | static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { | 5553 | static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { |
| 5544 | LLVMValueRef op = ir_llvm_value(g, instruction->op); | 5554 | LLVMValueRef op = ir_llvm_value(g, instruction->op); |
| 5545 | ZigType *int_type = instruction->base.value.type; | 5555 | ZigType *expr_type = instruction->base.value.type; |
| 5556 | bool is_vector = expr_type->id == ZigTypeIdVector; | ||
| 5557 | ZigType *int_type = is_vector ? expr_type->data.vector.elem_type : expr_type; | ||
| 5546 | assert(int_type->id == ZigTypeIdInt); | 5558 | assert(int_type->id == ZigTypeIdInt); |
| 5547 | if (int_type->data.integral.bit_count % 16 == 0) { | 5559 | if (int_type->data.integral.bit_count % 16 == 0) { |
| 5548 | LLVMValueRef fn_val = get_int_builtin_fn(g, instruction->base.value.type, BuiltinFnIdBswap); | 5560 | LLVMValueRef fn_val = get_int_builtin_fn(g, expr_type, BuiltinFnIdBswap); |
| 5549 | return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); | 5561 | return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); |
| 5550 | } | 5562 | } |
| 5551 | // Not an even number of bytes, so we zext 1 byte, then bswap, shift right 1 byte, truncate | 5563 | // Not an even number of bytes, so we zext 1 byte, then bswap, shift right 1 byte, truncate |
| 5552 | ZigType *extended_type = get_int_type(g, int_type->data.integral.is_signed, | 5564 | ZigType *extended_type = get_int_type(g, int_type->data.integral.is_signed, |
| 5553 | int_type->data.integral.bit_count + 8); | 5565 | int_type->data.integral.bit_count + 8); |
| 5566 | if (is_vector) | ||
| 5567 | extended_type = get_vector_type(g, expr_type->data.vector.len, extended_type); | ||
| 5554 | // aabbcc | 5568 | // aabbcc |
| 5555 | LLVMValueRef extended = LLVMBuildZExt(g->builder, op, get_llvm_type(g, extended_type), ""); | 5569 | LLVMValueRef extended = LLVMBuildZExt(g->builder, op, get_llvm_type(g, extended_type), ""); |
| 5556 | // 00aabbcc | 5570 | // 00aabbcc |
| ... | @@ -5560,7 +5574,7 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInst | ... | @@ -5560,7 +5574,7 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInst |
| 5560 | LLVMValueRef shifted = ZigLLVMBuildLShrExact(g->builder, swapped, | 5574 | LLVMValueRef shifted = ZigLLVMBuildLShrExact(g->builder, swapped, |
| 5561 | LLVMConstInt(get_llvm_type(g, extended_type), 8, false), ""); | 5575 | LLVMConstInt(get_llvm_type(g, extended_type), 8, false), ""); |
| 5562 | // 00ccbbaa | 5576 | // 00ccbbaa |
| 5563 | return LLVMBuildTrunc(g->builder, shifted, get_llvm_type(g, int_type), ""); | 5577 | return LLVMBuildTrunc(g->builder, shifted, get_llvm_type(g, expr_type), ""); |
| 5564 | } | 5578 | } |
| 5565 | 5579 | ||
| 5566 | static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable, IrInstructionBitReverse *instruction) { | 5580 | static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable, IrInstructionBitReverse *instruction) { |
src/ir.cpp+52-10| ... | @@ -25253,16 +25253,42 @@ static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstruct | ... | @@ -25253,16 +25253,42 @@ static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstruct |
| 25253 | } | 25253 | } |
| 25254 | 25254 | ||
| 25255 | static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) { | 25255 | static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) { |
| 25256 | ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); | 25256 | IrInstruction *op = instruction->op->child; |
| 25257 | if (type_is_invalid(int_type)) | 25257 | ZigType *type_expr = ir_resolve_type(ira, instruction->type->child); |
| 25258 | if (type_is_invalid(type_expr)) | ||
| 25258 | return ira->codegen->invalid_instruction; | 25259 | return ira->codegen->invalid_instruction; |
| 25259 | 25260 | ||
| 25260 | IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type); | 25261 | if (type_expr->id != ZigTypeIdInt) { |
| 25262 | ir_add_error(ira, instruction->type, | ||
| 25263 | buf_sprintf("expected integer type, found '%s'", buf_ptr(&type_expr->name))); | ||
| 25264 | if (type_expr->id == ZigTypeIdVector && | ||
| 25265 | type_expr->data.vector.elem_type->id == ZigTypeIdInt) | ||
| 25266 | ir_add_error(ira, instruction->type, | ||
| 25267 | buf_sprintf("represent vectors with their scalar types, i.e. '%s'", | ||
| 25268 | buf_ptr(&type_expr->data.vector.elem_type->name))); | ||
| 25269 | return ira->codegen->invalid_instruction; | ||
| 25270 | } | ||
| 25271 | ZigType *int_type = type_expr; | ||
| 25272 | |||
| 25273 | ZigType *expr_type = op->value.type; | ||
| 25274 | bool is_vector = expr_type->id == ZigTypeIdVector; | ||
| 25275 | ZigType *ret_type = int_type; | ||
| 25276 | if (is_vector) | ||
| 25277 | ret_type = get_vector_type(ira->codegen, expr_type->data.vector.len, int_type); | ||
| 25278 | |||
| 25279 | op = ir_implicit_cast(ira, instruction->op->child, ret_type); | ||
| 25261 | if (type_is_invalid(op->value.type)) | 25280 | if (type_is_invalid(op->value.type)) |
| 25262 | return ira->codegen->invalid_instruction; | 25281 | return ira->codegen->invalid_instruction; |
| 25263 | 25282 | ||
| 25264 | if (int_type->data.integral.bit_count == 0) { | 25283 | if (int_type->data.integral.bit_count == 0) { |
| 25265 | IrInstruction *result = ir_const(ira, &instruction->base, int_type); | 25284 | IrInstruction *result = ir_const(ira, &instruction->base, ret_type); |
| 25285 | if (is_vector) { | ||
| 25286 | expand_undef_array(ira->codegen, &result->value); | ||
| 25287 | result->value.data.x_array.data.s_none.elements = | ||
| 25288 | allocate<ConstExprValue>(expr_type->data.vector.len); | ||
| 25289 | for (unsigned i = 0; i < expr_type->data.vector.len; i++) | ||
| 25290 | bigint_init_unsigned(&result->value.data.x_array.data.s_none.elements[i].data.x_bigint, 0); | ||
| 25291 | } | ||
| 25266 | bigint_init_unsigned(&result->value.data.x_bigint, 0); | 25292 | bigint_init_unsigned(&result->value.data.x_bigint, 0); |
| 25267 | return result; | 25293 | return result; |
| 25268 | } | 25294 | } |
| ... | @@ -25282,20 +25308,36 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction | ... | @@ -25282,20 +25308,36 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction |
| 25282 | if (val == nullptr) | 25308 | if (val == nullptr) |
| 25283 | return ira->codegen->invalid_instruction; | 25309 | return ira->codegen->invalid_instruction; |
| 25284 | if (val->special == ConstValSpecialUndef) | 25310 | if (val->special == ConstValSpecialUndef) |
| 25285 | return ir_const_undef(ira, &instruction->base, int_type); | 25311 | return ir_const_undef(ira, &instruction->base, ret_type); |
| 25286 | 25312 | ||
| 25287 | IrInstruction *result = ir_const(ira, &instruction->base, int_type); | 25313 | IrInstruction *result = ir_const(ira, &instruction->base, ret_type); |
| 25288 | size_t buf_size = int_type->data.integral.bit_count / 8; | 25314 | size_t buf_size = int_type->data.integral.bit_count / 8; |
| 25289 | uint8_t *buf = allocate_nonzero<uint8_t>(buf_size); | 25315 | uint8_t *buf = allocate_nonzero<uint8_t>(buf_size); |
| 25290 | bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true); | 25316 | if (is_vector) { |
| 25291 | bigint_read_twos_complement(&result->value.data.x_bigint, buf, int_type->data.integral.bit_count, false, | 25317 | expand_undef_array(ira->codegen, &result->value); |
| 25292 | int_type->data.integral.is_signed); | 25318 | result->value.data.x_array.data.s_none.elements = |
| 25319 | allocate<ConstExprValue>(expr_type->data.vector.len); | ||
| 25320 | for (unsigned i = 0; i < expr_type->data.vector.len; i++) { | ||
| 25321 | ConstExprValue *cur = &val->data.x_array.data.s_none.elements[i]; | ||
| 25322 | result->value.data.x_array.data.s_none.elements[i].special = cur->special; | ||
| 25323 | if (cur->special == ConstValSpecialUndef) | ||
| 25324 | continue; | ||
| 25325 | bigint_write_twos_complement(&cur->data.x_bigint, buf, int_type->data.integral.bit_count, true); | ||
| 25326 | bigint_read_twos_complement(&result->value.data.x_array.data.s_none.elements[i].data.x_bigint, | ||
| 25327 | buf, int_type->data.integral.bit_count, false, | ||
| 25328 | int_type->data.integral.is_signed); | ||
| 25329 | } | ||
| 25330 | } else { | ||
| 25331 | bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true); | ||
| 25332 | bigint_read_twos_complement(&result->value.data.x_bigint, buf, int_type->data.integral.bit_count, false, | ||
| 25333 | int_type->data.integral.is_signed); | ||
| 25334 | } | ||
| 25293 | return result; | 25335 | return result; |
| 25294 | } | 25336 | } |
| 25295 | 25337 | ||
| 25296 | IrInstruction *result = ir_build_bswap(&ira->new_irb, instruction->base.scope, | 25338 | IrInstruction *result = ir_build_bswap(&ira->new_irb, instruction->base.scope, |
| 25297 | instruction->base.source_node, nullptr, op); | 25339 | instruction->base.source_node, nullptr, op); |
| 25298 | result->value.type = int_type; | 25340 | result->value.type = ret_type; |
| 25299 | return result; | 25341 | return result; |
| 25300 | } | 25342 | } |
| 25301 | 25343 |
test/stage1/behavior/byteswap.zig+11| ... | @@ -6,6 +6,11 @@ test "@byteSwap" { | ... | @@ -6,6 +6,11 @@ test "@byteSwap" { |
| 6 | testByteSwap(); | 6 | testByteSwap(); |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | test "@byteSwap on vectors" { | ||
| 10 | comptime testVectorByteSwap(); | ||
| 11 | testVectorByteSwap(); | ||
| 12 | } | ||
| 13 | |||
| 9 | fn testByteSwap() void { | 14 | fn testByteSwap() void { |
| 10 | expect(@byteSwap(u0, 0) == 0); | 15 | expect(@byteSwap(u0, 0) == 0); |
| 11 | expect(@byteSwap(u8, 0x12) == 0x12); | 16 | expect(@byteSwap(u8, 0x12) == 0x12); |
| ... | @@ -30,3 +35,9 @@ fn testByteSwap() void { | ... | @@ -30,3 +35,9 @@ fn testByteSwap() void { |
| 30 | expect(@byteSwap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == | 35 | expect(@byteSwap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == |
| 31 | @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); | 36 | @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); |
| 32 | } | 37 | } |
| 38 | |||
| 39 | fn testVectorByteSwap() void { | ||
| 40 | expect((@byteSwap(u8, @Vector(2, u8)([2]u8{0x12, 0x13})) == @Vector(2, u8)([2]u8{0x12, 0x13})).all); | ||
| 41 | expect((@byteSwap(u16, @Vector(2, u16)([2]u16{0x1234, 0x2345})) == @Vector(2, u16)([2]u16{0x3412, 0x4523})).all); | ||
| 42 | expect((@byteSwap(u24, @Vector(2, u24)([2]u24{0x123456, 0x234567})) == @Vector(2, u24)([2]u24{0x563412, 0x674523})).all); | ||
| 43 | } |