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) {
33713371}
33723372
33733373static 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
33763374 // Handle integers of non-pot bitsize by widening them.
33773375 const size_t bitsize = operand_type->data.integral.bit_count;
33783376 const bool is_signed = operand_type->data.integral.is_signed;
33793377 if (bitsize < 32 || !is_power_of_2(bitsize)) {
33803378 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);
33823380 value_ref = gen_widen_or_shorten(g, false, operand_type, wider_type, value_ref);
33833381 operand_type = wider_type;
33843382 }
......@@ -3395,35 +3393,22 @@ static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref,
33953393 }
33963394
33973395 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);
3399
3400 LLVMValueRef result;
3401 if (vector_len == 0) {
3402 LLVMValueRef params[1] = {value_ref};
3403 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3396 LLVMValueRef func_ref;
3397 if ((operand_type->data.integral.bit_count == 128) && (g->zig_target->os == OsWindows) && (g->zig_target->arch == ZigLLVM_x86_64)) {
3398 // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard i128 calling
3399 // convention to adhere to the ABI that LLVM expects compiler-rt to have.
3400 LLVMTypeRef v2i64 = LLVMVectorType(LLVMInt64Type(), 2);
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);
34043403 } else {
3405 ZigType *alloca_ty = operand_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, "");
3404 func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, result_type->llvm_type);
34203405 }
3421 return result;
3406
3407 LLVMValueRef params[1] = {value_ref};
3408 return LLVMBuildCall(g->builder, func_ref, params, param_count, "");
34223409}
34233410
34243411static 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
34273412 // Handle integers of non-pot bitsize by truncating a sufficiently wide pot integer
34283413 const size_t bitsize = result_type->data.integral.bit_count;
34293414 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,
34453430 }
34463431
34473432 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);
3449
3450 LLVMValueRef result;
3451 if (vector_len == 0) {
3452 LLVMValueRef params[1] = {value_ref};
3453 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3433 LLVMValueRef func_ref;
3434 if ((wider_type->data.integral.bit_count == 128) && (g->zig_target->os == OsWindows) && (g->zig_target->arch == ZigLLVM_x86_64)) {
3435 // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard i128 calling
3436 // convention to adhere to the ABI that LLVM expects compiler-rt to have.
3437 LLVMTypeRef v2i64 = LLVMVectorType(LLVMInt64Type(), 2);
3438 func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, v2i64);
34543439 } else {
3455 ZigType *alloca_ty = operand_type;
3456 result = build_alloca(g, alloca_ty, "", 0);
3440 func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, wider_type->llvm_type);
3441 }
34573442
3458 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
3459 for (uint32_t i = 0; i < vector_len; i++) {
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 }
3443 LLVMValueRef params[1] = {value_ref};
3444 LLVMValueRef result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
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, "");
34703448 }
34713449
34723450 // Handle integers of non-pot bitsize by shortening them on the output
34733451 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);
34753453 }
3454
34763455 return result;
34773456}
34783457
34793458static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LLVMValueRef op2_value, ZigType *operand_type, IrBinOp op_id) {
34803459 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
34813460
3482 LLVMTypeRef return_type = operand_type->llvm_type;
34833461 int param_count = 2;
34843462
3485 const char *compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type);
3486 const char *math_float_prefix = libc_float_prefix(g, operand_type);
3487 const char *math_float_suffix = libc_float_suffix(g, operand_type);
3463 ZigType *operand_scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
3464 LLVMTypeRef return_scalar_type = operand_scalar_type->llvm_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
34893469 char fn_name[64];
34903470 Icmp res_icmp = NONE;
......@@ -3511,32 +3491,32 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL
35113491 case IrBinOpShlSat:
35123492 zig_unreachable();
35133493 case IrBinOpCmpEq:
3514 return_type = g->builtin_types.entry_i32->llvm_type;
3494 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
35153495 snprintf(fn_name, sizeof(fn_name), "__eq%sf2", compiler_rt_type_abbrev);
35163496 res_icmp = EQ_ZERO;
35173497 break;
35183498 case IrBinOpCmpNotEq:
3519 return_type = g->builtin_types.entry_i32->llvm_type;
3499 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
35203500 snprintf(fn_name, sizeof(fn_name), "__ne%sf2", compiler_rt_type_abbrev);
35213501 res_icmp = NE_ZERO;
35223502 break;
35233503 case IrBinOpCmpLessOrEq:
3524 return_type = g->builtin_types.entry_i32->llvm_type;
3504 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
35253505 snprintf(fn_name, sizeof(fn_name), "__le%sf2", compiler_rt_type_abbrev);
35263506 res_icmp = LE_ZERO;
35273507 break;
35283508 case IrBinOpCmpLessThan:
3529 return_type = g->builtin_types.entry_i32->llvm_type;
3509 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
35303510 snprintf(fn_name, sizeof(fn_name), "__le%sf2", compiler_rt_type_abbrev);
35313511 res_icmp = EQ_NEG;
35323512 break;
35333513 case IrBinOpCmpGreaterOrEq:
3534 return_type = g->builtin_types.entry_i32->llvm_type;
3514 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
35353515 snprintf(fn_name, sizeof(fn_name), "__ge%sf2", compiler_rt_type_abbrev);
35363516 res_icmp = GE_ZERO;
35373517 break;
35383518 case IrBinOpCmpGreaterThan:
3539 return_type = g->builtin_types.entry_i32->llvm_type;
3519 return_scalar_type = g->builtin_types.entry_i32->llvm_type;
35403520 snprintf(fn_name, sizeof(fn_name), "__ge%sf2", compiler_rt_type_abbrev);
35413521 res_icmp = EQ_ONE;
35423522 break;
......@@ -3569,7 +3549,7 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL
35693549 zig_unreachable();
35703550 }
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
35743554 LLVMValueRef result;
35753555 if (vector_len == 0) {
test/behavior/vector.zig+14-12
......@@ -101,18 +101,20 @@ test "vector float operators" {
101101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
102102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
103103
104 const S = struct {
105 fn doTheTest() !void {
106 var v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 };
107 var x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 };
108 try expect(mem.eql(f32, &@as([4]f32, v + x), &[4]f32{ 11, 22, 33, 44 }));
109 try expect(mem.eql(f32, &@as([4]f32, v - x), &[4]f32{ 9, 18, 27, 36 }));
110 try expect(mem.eql(f32, &@as([4]f32, v * x), &[4]f32{ 10, 40, 90, 160 }));
111 try expect(mem.eql(f32, &@as([4]f32, -x), &[4]f32{ -1, -2, -3, -4 }));
112 }
113 };
114 try S.doTheTest();
115 comptime try S.doTheTest();
104 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
105 const S = struct {
106 fn doTheTest() !void {
107 var v: @Vector(4, T) = [4]T{ 10, 20, 30, 40 };
108 var x: @Vector(4, T) = [4]T{ 1, 2, 3, 4 };
109 try expect(mem.eql(T, &@as([4]T, v + x), &[4]T{ 11, 22, 33, 44 }));
110 try expect(mem.eql(T, &@as([4]T, v - x), &[4]T{ 9, 18, 27, 36 }));
111 try expect(mem.eql(T, &@as([4]T, v * x), &[4]T{ 10, 40, 90, 160 }));
112 try expect(mem.eql(T, &@as([4]T, -x), &[4]T{ -1, -2, -3, -4 }));
113 }
114 };
115 try S.doTheTest();
116 comptime try S.doTheTest();
117 }
116118}
117119
118120test "vector bit operators" {