authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-19 19:32:26-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-19 19:32:45-07:00
log78389af55266d58e699a4d982e00afb6cce7ec64
treefdb479dc59ff6e65c06d14866be3ac848965201e
parent95e135a8cb614f1ef8a8bbe38717fc533c2c2cef

LLVM: add valgrind integration for x86 and aarch64

This also modifies the inline assembly to be more optimizable - instead of doing explicit movs, we instead communicate to LLVM which registers we would like to, somehow, have the correct values. This is how the x86_64 code already worked and thus allows the code to be unified across the two architectures. As a bonus, I threw in x86 support.

3 files changed, 82 insertions(+), 62 deletions(-)

lib/std/valgrind.zig+4-7
......@@ -32,16 +32,13 @@ pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3:
3232 },
3333 .aarch64 => {
3434 return asm volatile (
35 \\ mov x3, %[default]
36 \\ mov x4, %[ptr]
3735 \\ ror x12, x12, #3 ; ror x12, x12, #13
3836 \\ ror x12, x12, #51 ; ror x12, x12, #61
3937 \\ orr x10, x10, x10
40 \\ mov %[ret], x3
41 : [ret] "=r" (-> usize),
42 : [default] "r" (default),
43 [ptr] "r" (&[_]usize{ request, a1, a2, a3, a4, a5 }),
44 : "cc", "memory", "x3", "x4"
38 : [_] "={x3}" (-> usize),
39 : [_] "{x4}" (&[_]usize{ request, a1, a2, a3, a4, a5 }),
40 [_] "0" (default),
41 : "cc", "memory"
4542 );
4643 },
4744 // ppc32
src/codegen/llvm.zig+72-54
......@@ -9832,62 +9832,80 @@ pub const FuncGen = struct {
98329832 const usize_llvm_ty = fg.context.intType(target.cpu.arch.ptrBitWidth());
98339833 const usize_alignment = @intCast(c_uint, Type.usize.abiSize(target));
98349834
9835 switch (target.cpu.arch) {
9836 .x86_64 => {
9837 const array_llvm_ty = usize_llvm_ty.arrayType(6);
9838 const array_ptr = fg.valgrind_client_request_array orelse a: {
9839 const array_ptr = fg.buildAlloca(array_llvm_ty, usize_alignment);
9840 fg.valgrind_client_request_array = array_ptr;
9841 break :a array_ptr;
9842 };
9843 const array_elements = [_]*llvm.Value{ request, a1, a2, a3, a4, a5 };
9844 const zero = usize_llvm_ty.constInt(0, .False);
9845 for (array_elements) |elem, i| {
9846 const indexes = [_]*llvm.Value{
9847 zero, usize_llvm_ty.constInt(@intCast(c_uint, i), .False),
9848 };
9849 const elem_ptr = fg.builder.buildInBoundsGEP(array_llvm_ty, array_ptr, &indexes, indexes.len, "");
9850 const store_inst = fg.builder.buildStore(elem, elem_ptr);
9851 store_inst.setAlignment(usize_alignment);
9852 }
9853
9854 const asm_template =
9855 \\rolq $$3, %rdi ; rolq $$13, %rdi
9856 \\rolq $$61, %rdi ; rolq $$51, %rdi
9857 \\xchgq %rbx,%rbx
9858 ;
9859
9860 const asm_constraints = "={rdx},{rax},0,~{cc},~{memory}";
9861
9862 const array_ptr_as_usize = fg.builder.buildPtrToInt(array_ptr, usize_llvm_ty, "");
9863 const args = [_]*llvm.Value{ array_ptr_as_usize, default_value };
9864 const param_types = [_]*llvm.Type{ usize_llvm_ty, usize_llvm_ty };
9865 const fn_llvm_ty = llvm.functionType(usize_llvm_ty, &param_types, args.len, .False);
9866 const asm_fn = llvm.getInlineAsm(
9867 fn_llvm_ty,
9868 asm_template,
9869 asm_template.len,
9870 asm_constraints,
9871 asm_constraints.len,
9872 .True, // has side effects
9873 .False, // alignstack
9874 .ATT,
9875 .False,
9876 );
9877
9878 const call = fg.builder.buildCall(
9879 fn_llvm_ty,
9880 asm_fn,
9881 &args,
9882 args.len,
9883 .C,
9884 .Auto,
9885 "",
9886 );
9887 return call;
9835 const array_llvm_ty = usize_llvm_ty.arrayType(6);
9836 const array_ptr = fg.valgrind_client_request_array orelse a: {
9837 const array_ptr = fg.buildAlloca(array_llvm_ty, usize_alignment);
9838 fg.valgrind_client_request_array = array_ptr;
9839 break :a array_ptr;
9840 };
9841 const array_elements = [_]*llvm.Value{ request, a1, a2, a3, a4, a5 };
9842 const zero = usize_llvm_ty.constInt(0, .False);
9843 for (array_elements) |elem, i| {
9844 const indexes = [_]*llvm.Value{
9845 zero, usize_llvm_ty.constInt(@intCast(c_uint, i), .False),
9846 };
9847 const elem_ptr = fg.builder.buildInBoundsGEP(array_llvm_ty, array_ptr, &indexes, indexes.len, "");
9848 const store_inst = fg.builder.buildStore(elem, elem_ptr);
9849 store_inst.setAlignment(usize_alignment);
9850 }
9851
9852 const arch_specific: struct {
9853 template: [:0]const u8,
9854 constraints: [:0]const u8,
9855 } = switch (target.cpu.arch) {
9856 .x86 => .{
9857 .template =
9858 \\roll $$3, %edi ; roll $$13, %edi
9859 \\roll $$61, %edi ; roll $$51, %edi
9860 \\xchgl %ebx,%ebx
9861 ,
9862 .constraints = "={edx},{eax},0,~{cc},~{memory}",
9863 },
9864 .x86_64 => .{
9865 .template =
9866 \\rolq $$3, %rdi ; rolq $$13, %rdi
9867 \\rolq $$61, %rdi ; rolq $$51, %rdi
9868 \\xchgq %rbx,%rbx
9869 ,
9870 .constraints = "={rdx},{rax},0,~{cc},~{memory}",
9871 },
9872 .aarch64, .aarch64_32, .aarch64_be => .{
9873 .template =
9874 \\ror x12, x12, #3 ; ror x12, x12, #13
9875 \\ror x12, x12, #51 ; ror x12, x12, #61
9876 \\orr x10, x10, x10
9877 ,
9878 .constraints = "={x3},{x4},0,~{cc},~{memory}",
98889879 },
98899880 else => unreachable,
9890 }
9881 };
9882
9883 const array_ptr_as_usize = fg.builder.buildPtrToInt(array_ptr, usize_llvm_ty, "");
9884 const args = [_]*llvm.Value{ array_ptr_as_usize, default_value };
9885 const param_types = [_]*llvm.Type{ usize_llvm_ty, usize_llvm_ty };
9886 const fn_llvm_ty = llvm.functionType(usize_llvm_ty, &param_types, args.len, .False);
9887 const asm_fn = llvm.getInlineAsm(
9888 fn_llvm_ty,
9889 arch_specific.template.ptr,
9890 arch_specific.template.len,
9891 arch_specific.constraints.ptr,
9892 arch_specific.constraints.len,
9893 .True, // has side effects
9894 .False, // alignstack
9895 .ATT,
9896 .False, // can throw
9897 );
9898
9899 const call = fg.builder.buildCall(
9900 fn_llvm_ty,
9901 asm_fn,
9902 &args,
9903 args.len,
9904 .C,
9905 .Auto,
9906 "",
9907 );
9908 return call;
98919909 }
98929910};
98939911
src/target.zig+6-1
......@@ -211,7 +211,12 @@ pub fn isSingleThreaded(target: std.Target) bool {
211211/// Valgrind supports more, but Zig does not support them yet.
212212pub fn hasValgrindSupport(target: std.Target) bool {
213213 switch (target.cpu.arch) {
214 .x86_64 => {
214 .x86,
215 .x86_64,
216 .aarch64,
217 .aarch64_32,
218 .aarch64_be,
219 => {
215220 return target.os.tag == .linux or target.os.tag == .solaris or
216221 (target.os.tag == .windows and target.abi != .msvc);
217222 },