authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-07-10 16:46:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-10 20:52:28-07:00
logd182e2ebda89e65f6503eadc21bdb0e600f21ea1
treed898df8314b2f96b4dccbadcdec6410f61fd05ba
parentbb8971150c6844bbb6eaee3687068bf2d4923710

stage1: Lower libcalls on Windows x86-64 correctly

This change is the Zig counterpart to https://reviews.llvm.org/D110413 Since we lower some libcalls directly (just like clang does), we need to make sure that the ABI we call with matches the ABI of the compiler-rt we are providing (and also the ABI expected by LLVM). While I was at it, I noticed some flawed vector handling in the binary soft float ops in stage 1, so I shored up the logic a bit and expanded an existing test to cover the missing functionality.

2 files changed, 52 insertions(+), 70 deletions(-)

src/stage1/codegen.cpp+38-58
...@@ -3371,14 +3371,12 @@ static LLVMValueRef add_icmp(CodeGen *g, LLVMValueRef val, Icmp kind) {...@@ -3371,14 +3371,12 @@ static LLVMValueRef add_icmp(CodeGen *g, LLVMValueRef val, Icmp kind) {
3371}3371}
33723372
3373static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) {3373static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) {
3374 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
3375
3376 // Handle integers of non-pot bitsize by widening them.3374 // Handle integers of non-pot bitsize by widening them.
3377 const size_t bitsize = operand_type->data.integral.bit_count;3375 const size_t bitsize = operand_type->data.integral.bit_count;
3378 const bool is_signed = operand_type->data.integral.is_signed;3376 const bool is_signed = operand_type->data.integral.is_signed;
3379 if (bitsize < 32 || !is_power_of_2(bitsize)) {3377 if (bitsize < 32 || !is_power_of_2(bitsize)) {
3380 const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize);3378 const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize);
3381 ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize);3379 ZigType *wider_type = get_int_type(g, is_signed, wider_bitsize);
3382 value_ref = gen_widen_or_shorten(g, false, operand_type, wider_type, value_ref);3380 value_ref = gen_widen_or_shorten(g, false, operand_type, wider_type, value_ref);
3383 operand_type = wider_type;3381 operand_type = wider_type;
3384 }3382 }
...@@ -3395,35 +3393,22 @@ static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref,...@@ -3395,35 +3393,22 @@ static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref,
3395 }3393 }
33963394
3397 int param_count = 1;3395 int param_count = 1;
3398 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, result_type->llvm_type);3396 LLVMValueRef func_ref;
33993397 if ((operand_type->data.integral.bit_count == 128) && (g->zig_target->os == OsWindows) && (g->zig_target->arch == ZigLLVM_x86_64)) {
3400 LLVMValueRef result;3398 // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard i128 calling
3401 if (vector_len == 0) {3399 // convention to adhere to the ABI that LLVM expects compiler-rt to have.
3402 LLVMValueRef params[1] = {value_ref};3400 LLVMTypeRef v2i64 = LLVMVectorType(LLVMInt64Type(), 2);
3403 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");3401 value_ref = LLVMBuildBitCast(g->builder, value_ref, v2i64, "");
3402 func_ref = get_soft_float_fn(g, fn_name, param_count, v2i64, result_type->llvm_type);
3404 } else {3403 } else {
3405 ZigType *alloca_ty = operand_type;3404 func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, result_type->llvm_type);
3406 result = build_alloca(g, alloca_ty, "", 0);
3407
3408 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
3409 for (uint32_t i = 0; i < vector_len; i++) {
3410 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3411 LLVMValueRef params[1] = {
3412 LLVMBuildExtractElement(g->builder, value_ref, index_value, ""),
3413 };
3414 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3415 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3416 call_result, index_value, "");
3417 }
3418
3419 result = LLVMBuildLoad(g->builder, result, "");
3420 }3405 }
3421 return result;3406
3407 LLVMValueRef params[1] = {value_ref};
3408 return LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3422}3409}
34233410
3424static LLVMValueRef gen_soft_float_to_int_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) {3411static LLVMValueRef gen_soft_float_to_int_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) {
3425 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
3426
3427 // Handle integers of non-pot bitsize by truncating a sufficiently wide pot integer3412 // Handle integers of non-pot bitsize by truncating a sufficiently wide pot integer
3428 const size_t bitsize = result_type->data.integral.bit_count;3413 const size_t bitsize = result_type->data.integral.bit_count;
3429 const bool is_signed = result_type->data.integral.is_signed;3414 const bool is_signed = result_type->data.integral.is_signed;
...@@ -3445,46 +3430,41 @@ static LLVMValueRef gen_soft_float_to_int_op(CodeGen *g, LLVMValueRef value_ref,...@@ -3445,46 +3430,41 @@ static LLVMValueRef gen_soft_float_to_int_op(CodeGen *g, LLVMValueRef value_ref,
3445 }3430 }
34463431
3447 int param_count = 1;3432 int param_count = 1;
3448 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, wider_type->llvm_type);3433 LLVMValueRef func_ref;
34493434 if ((wider_type->data.integral.bit_count == 128) && (g->zig_target->os == OsWindows) && (g->zig_target->arch == ZigLLVM_x86_64)) {
3450 LLVMValueRef result;3435 // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard i128 calling
3451 if (vector_len == 0) {3436 // convention to adhere to the ABI that LLVM expects compiler-rt to have.
3452 LLVMValueRef params[1] = {value_ref};3437 LLVMTypeRef v2i64 = LLVMVectorType(LLVMInt64Type(), 2);
3453 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");3438 func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, v2i64);
3454 } else {3439 } else {
3455 ZigType *alloca_ty = operand_type;3440 func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, wider_type->llvm_type);
3456 result = build_alloca(g, alloca_ty, "", 0);3441 }
34573442
3458 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;3443 LLVMValueRef params[1] = {value_ref};
3459 for (uint32_t i = 0; i < vector_len; i++) {3444 LLVMValueRef result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3460 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3461 LLVMValueRef params[1] = {
3462 LLVMBuildExtractElement(g->builder, value_ref, index_value, ""),
3463 };
3464 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3465 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3466 call_result, index_value, "");
3467 }
34683445
3469 result = LLVMBuildLoad(g->builder, result, "");3446 if ((wider_type->data.integral.bit_count == 128) && (g->zig_target->os == OsWindows) && (g->zig_target->arch == ZigLLVM_x86_64)) {
3447 result = LLVMBuildBitCast(g->builder, result, wider_type->llvm_type, "");
3470 }3448 }
34713449
3472 // Handle integers of non-pot bitsize by shortening them on the output3450 // Handle integers of non-pot bitsize by shortening them on the output
3473 if (result_type != wider_type) {3451 if (result_type != wider_type) {
3474 return gen_widen_or_shorten(g, false, wider_type, result_type, result);3452 result = gen_widen_or_shorten(g, false, wider_type, result_type, result);
3475 }3453 }
3454
3476 return result;3455 return result;
3477}3456}
34783457
3479static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LLVMValueRef op2_value, ZigType *operand_type, IrBinOp op_id) {3458static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LLVMValueRef op2_value, ZigType *operand_type, IrBinOp op_id) {
3480 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;3459 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
34813460
3482 LLVMTypeRef return_type = operand_type->llvm_type;
3483 int param_count = 2;3461 int param_count = 2;
34843462
3485 const char *compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type);3463 ZigType *operand_scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
3486 const char *math_float_prefix = libc_float_prefix(g, operand_type);3464 LLVMTypeRef return_scalar_type = operand_scalar_type->llvm_type;
3487 const char *math_float_suffix = libc_float_suffix(g, operand_type);3465 const char *compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_scalar_type);
3466 const char *math_float_prefix = libc_float_prefix(g, operand_scalar_type);
3467 const char *math_float_suffix = libc_float_suffix(g, operand_scalar_type);
34883468
3489 char fn_name[64];3469 char fn_name[64];
3490 Icmp res_icmp = NONE;3470 Icmp res_icmp = NONE;
...@@ -3511,32 +3491,32 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL...@@ -3511,32 +3491,32 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL
3511 case IrBinOpShlSat:3491 case IrBinOpShlSat:
3512 zig_unreachable();3492 zig_unreachable();
3513 case IrBinOpCmpEq:3493 case IrBinOpCmpEq:
3514 return_type = g->builtin_types.entry_i32->llvm_type;3494 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
3515 snprintf(fn_name, sizeof(fn_name), "__eq%sf2", compiler_rt_type_abbrev);3495 snprintf(fn_name, sizeof(fn_name), "__eq%sf2", compiler_rt_type_abbrev);
3516 res_icmp = EQ_ZERO;3496 res_icmp = EQ_ZERO;
3517 break;3497 break;
3518 case IrBinOpCmpNotEq:3498 case IrBinOpCmpNotEq:
3519 return_type = g->builtin_types.entry_i32->llvm_type;3499 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
3520 snprintf(fn_name, sizeof(fn_name), "__ne%sf2", compiler_rt_type_abbrev);3500 snprintf(fn_name, sizeof(fn_name), "__ne%sf2", compiler_rt_type_abbrev);
3521 res_icmp = NE_ZERO;3501 res_icmp = NE_ZERO;
3522 break;3502 break;
3523 case IrBinOpCmpLessOrEq:3503 case IrBinOpCmpLessOrEq:
3524 return_type = g->builtin_types.entry_i32->llvm_type;3504 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
3525 snprintf(fn_name, sizeof(fn_name), "__le%sf2", compiler_rt_type_abbrev);3505 snprintf(fn_name, sizeof(fn_name), "__le%sf2", compiler_rt_type_abbrev);
3526 res_icmp = LE_ZERO;3506 res_icmp = LE_ZERO;
3527 break;3507 break;
3528 case IrBinOpCmpLessThan:3508 case IrBinOpCmpLessThan:
3529 return_type = g->builtin_types.entry_i32->llvm_type;3509 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
3530 snprintf(fn_name, sizeof(fn_name), "__le%sf2", compiler_rt_type_abbrev);3510 snprintf(fn_name, sizeof(fn_name), "__le%sf2", compiler_rt_type_abbrev);
3531 res_icmp = EQ_NEG;3511 res_icmp = EQ_NEG;
3532 break;3512 break;
3533 case IrBinOpCmpGreaterOrEq:3513 case IrBinOpCmpGreaterOrEq:
3534 return_type = g->builtin_types.entry_i32->llvm_type;3514 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
3535 snprintf(fn_name, sizeof(fn_name), "__ge%sf2", compiler_rt_type_abbrev);3515 snprintf(fn_name, sizeof(fn_name), "__ge%sf2", compiler_rt_type_abbrev);
3536 res_icmp = GE_ZERO;3516 res_icmp = GE_ZERO;
3537 break;3517 break;
3538 case IrBinOpCmpGreaterThan:3518 case IrBinOpCmpGreaterThan:
3539 return_type = g->builtin_types.entry_i32->llvm_type;3519 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
3540 snprintf(fn_name, sizeof(fn_name), "__ge%sf2", compiler_rt_type_abbrev);3520 snprintf(fn_name, sizeof(fn_name), "__ge%sf2", compiler_rt_type_abbrev);
3541 res_icmp = EQ_ONE;3521 res_icmp = EQ_ONE;
3542 break;3522 break;
...@@ -3569,7 +3549,7 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL...@@ -3569,7 +3549,7 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL
3569 zig_unreachable();3549 zig_unreachable();
3570 }3550 }
35713551
3572 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, return_type);3552 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_scalar_type->llvm_type, return_scalar_type);
35733553
3574 LLVMValueRef result;3554 LLVMValueRef result;
3575 if (vector_len == 0) {3555 if (vector_len == 0) {
test/behavior/vector.zig+14-12
...@@ -101,18 +101,20 @@ test "vector float operators" {...@@ -101,18 +101,20 @@ test "vector float operators" {
101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
103103
104 const S = struct {104 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
105 fn doTheTest() !void {105 const S = struct {
106 var v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 };106 fn doTheTest() !void {
107 var x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 };107 var v: @Vector(4, T) = [4]T{ 10, 20, 30, 40 };
108 try expect(mem.eql(f32, &@as([4]f32, v + x), &[4]f32{ 11, 22, 33, 44 }));108 var x: @Vector(4, T) = [4]T{ 1, 2, 3, 4 };
109 try expect(mem.eql(f32, &@as([4]f32, v - x), &[4]f32{ 9, 18, 27, 36 }));109 try expect(mem.eql(T, &@as([4]T, v + x), &[4]T{ 11, 22, 33, 44 }));
110 try expect(mem.eql(f32, &@as([4]f32, v * x), &[4]f32{ 10, 40, 90, 160 }));110 try expect(mem.eql(T, &@as([4]T, v - x), &[4]T{ 9, 18, 27, 36 }));
111 try expect(mem.eql(f32, &@as([4]f32, -x), &[4]f32{ -1, -2, -3, -4 }));111 try expect(mem.eql(T, &@as([4]T, v * x), &[4]T{ 10, 40, 90, 160 }));
112 }112 try expect(mem.eql(T, &@as([4]T, -x), &[4]T{ -1, -2, -3, -4 }));
113 };113 }
114 try S.doTheTest();114 };
115 comptime try S.doTheTest();115 try S.doTheTest();
116 comptime try S.doTheTest();
117 }
116}118}
117119
118test "vector bit operators" {120test "vector bit operators" {