| ... | ... | @@ -1894,6 +1894,91 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { |
| 1894 | 1894 | report_errors_and_exit(g); |
| 1895 | 1895 | } |
| 1896 | 1896 | |
| 1897 | static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { |
| 1898 | assert(alignment > 0); |
| 1899 | LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); |
| 1900 | LLVMSetAlignment(result, alignment); |
| 1901 | return result; |
| 1902 | } |
| 1903 | |
| 1904 | enum X64CABIClass { |
| 1905 | X64CABIClass_Unknown, |
| 1906 | X64CABIClass_MEMORY, |
| 1907 | X64CABIClass_INTEGER, |
| 1908 | X64CABIClass_SSE, |
| 1909 | }; |
| 1910 | |
| 1911 | static X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { |
| 1912 | size_t ty_size = type_size(g, ty); |
| 1913 | if (get_codegen_ptr_type(ty) != nullptr) |
| 1914 | return X64CABIClass_INTEGER; |
| 1915 | switch (ty->id) { |
| 1916 | case ZigTypeIdEnum: |
| 1917 | case ZigTypeIdInt: |
| 1918 | case ZigTypeIdBool: |
| 1919 | return X64CABIClass_INTEGER; |
| 1920 | case ZigTypeIdFloat: |
| 1921 | return X64CABIClass_SSE; |
| 1922 | case ZigTypeIdStruct: { |
| 1923 | // "If the size of an object is larger than four eightbytes, or it contains unaligned |
| 1924 | // fields, it has class MEMORY" |
| 1925 | if (ty_size > 32) |
| 1926 | return X64CABIClass_MEMORY; |
| 1927 | if (ty->data.structure.layout != ContainerLayoutExtern) { |
| 1928 | // TODO determine whether packed structs have any unaligned fields |
| 1929 | return X64CABIClass_Unknown; |
| 1930 | } |
| 1931 | // "If the size of the aggregate exceeds two eightbytes and the first eight- |
| 1932 | // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument |
| 1933 | // is passed in memory." |
| 1934 | if (ty_size > 16) { |
| 1935 | // Zig doesn't support vectors and large fp registers yet, so this will always |
| 1936 | // be memory. |
| 1937 | return X64CABIClass_MEMORY; |
| 1938 | } |
| 1939 | X64CABIClass working_class = X64CABIClass_Unknown; |
| 1940 | for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { |
| 1941 | X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry); |
| 1942 | if (field_class == X64CABIClass_Unknown) |
| 1943 | return X64CABIClass_Unknown; |
| 1944 | if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { |
| 1945 | working_class = field_class; |
| 1946 | } |
| 1947 | } |
| 1948 | return working_class; |
| 1949 | } |
| 1950 | case ZigTypeIdUnion: { |
| 1951 | // "If the size of an object is larger than four eightbytes, or it contains unaligned |
| 1952 | // fields, it has class MEMORY" |
| 1953 | if (ty_size > 32) |
| 1954 | return X64CABIClass_MEMORY; |
| 1955 | if (ty->data.unionation.layout != ContainerLayoutExtern) |
| 1956 | return X64CABIClass_MEMORY; |
| 1957 | // "If the size of the aggregate exceeds two eightbytes and the first eight- |
| 1958 | // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument |
| 1959 | // is passed in memory." |
| 1960 | if (ty_size > 16) { |
| 1961 | // Zig doesn't support vectors and large fp registers yet, so this will always |
| 1962 | // be memory. |
| 1963 | return X64CABIClass_MEMORY; |
| 1964 | } |
| 1965 | X64CABIClass working_class = X64CABIClass_Unknown; |
| 1966 | for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { |
| 1967 | X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry); |
| 1968 | if (field_class == X64CABIClass_Unknown) |
| 1969 | return X64CABIClass_Unknown; |
| 1970 | if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { |
| 1971 | working_class = field_class; |
| 1972 | } |
| 1973 | } |
| 1974 | return working_class; |
| 1975 | } |
| 1976 | default: |
| 1977 | return X64CABIClass_Unknown; |
| 1978 | } |
| 1979 | } |
| 1980 | |
| 1981 | // NOTE this does not depend on x86_64 |
| 1897 | 1982 | static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { |
| 1898 | 1983 | size_t ty_size = type_size(g, ty); |
| 1899 | 1984 | if (ty_size > g->pointer_size_bytes) |
| ... | ... | @@ -1905,13 +1990,6 @@ static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { |
| 1905 | 1990 | get_codegen_ptr_type(ty) != nullptr); |
| 1906 | 1991 | } |
| 1907 | 1992 | |
| 1908 | | static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { |
| 1909 | | assert(alignment > 0); |
| 1910 | | LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); |
| 1911 | | LLVMSetAlignment(result, alignment); |
| 1912 | | return result; |
| 1913 | | } |
| 1914 | | |
| 1915 | 1993 | static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { |
| 1916 | 1994 | // Initialized from the type for some walks, but because of C var args, |
| 1917 | 1995 | // initialized based on callsite instructions for that one. |
| ... | ... | @@ -2047,98 +2125,79 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ |
| 2047 | 2125 | } |
| 2048 | 2126 | |
| 2049 | 2127 | if (g->zig_target.arch.arch == ZigLLVM_x86_64) { |
| 2128 | X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); |
| 2050 | 2129 | size_t ty_size = type_size(g, ty); |
| 2051 | | if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { |
| 2130 | if (abi_class == X64CABIClass_MEMORY) { |
| 2052 | 2131 | assert(handle_is_ptr(ty)); |
| 2053 | | |
| 2054 | | // "If the size of an object is larger than four eightbytes, or it contains unaligned |
| 2055 | | // fields, it has class MEMORY" |
| 2056 | | if (ty_size > 32) { |
| 2057 | | switch (fn_walk->id) { |
| 2058 | | case FnWalkIdAttrs: |
| 2059 | | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); |
| 2060 | | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); |
| 2061 | | fn_walk->data.attrs.gen_i += 1; |
| 2062 | | break; |
| 2063 | | case FnWalkIdCall: |
| 2064 | | fn_walk->data.call.gen_param_values->append(val); |
| 2065 | | break; |
| 2066 | | case FnWalkIdTypes: { |
| 2067 | | ZigType *gen_type = get_pointer_to_type(g, ty, true); |
| 2068 | | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); |
| 2069 | | fn_walk->data.types.param_di_types->append(gen_type->di_type); |
| 2070 | | break; |
| 2071 | | } |
| 2072 | | case FnWalkIdVars: { |
| 2073 | | di_arg_index = fn_walk->data.vars.gen_i; |
| 2074 | | var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); |
| 2075 | | dest_ty = get_pointer_to_type(g, ty, false); |
| 2076 | | fn_walk->data.vars.gen_i += 1; |
| 2077 | | goto var_ok; |
| 2078 | | } |
| 2079 | | case FnWalkIdInits: |
| 2080 | | if (var->decl_node) { |
| 2081 | | gen_var_debug_decl(g, var); |
| 2082 | | } |
| 2083 | | fn_walk->data.inits.gen_i += 1; |
| 2084 | | break; |
| 2132 | switch (fn_walk->id) { |
| 2133 | case FnWalkIdAttrs: |
| 2134 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); |
| 2135 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); |
| 2136 | fn_walk->data.attrs.gen_i += 1; |
| 2137 | break; |
| 2138 | case FnWalkIdCall: |
| 2139 | fn_walk->data.call.gen_param_values->append(val); |
| 2140 | break; |
| 2141 | case FnWalkIdTypes: { |
| 2142 | ZigType *gen_type = get_pointer_to_type(g, ty, true); |
| 2143 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); |
| 2144 | fn_walk->data.types.param_di_types->append(gen_type->di_type); |
| 2145 | break; |
| 2085 | 2146 | } |
| 2086 | | return true; |
| 2087 | | } |
| 2088 | | } |
| 2089 | | if (ty->id == ZigTypeIdStruct) { |
| 2090 | | assert(handle_is_ptr(ty)); |
| 2091 | | // "If the size of the aggregate exceeds a single eightbyte, each is classified |
| 2092 | | // separately. Each eightbyte gets initialized to class NO_CLASS." |
| 2093 | | if (ty_size <= 8) { |
| 2094 | | bool contains_int = false; |
| 2095 | | for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { |
| 2096 | | if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { |
| 2097 | | contains_int = true; |
| 2098 | | break; |
| 2147 | case FnWalkIdVars: { |
| 2148 | di_arg_index = fn_walk->data.vars.gen_i; |
| 2149 | var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); |
| 2150 | dest_ty = get_pointer_to_type(g, ty, false); |
| 2151 | fn_walk->data.vars.gen_i += 1; |
| 2152 | goto var_ok; |
| 2153 | } |
| 2154 | case FnWalkIdInits: |
| 2155 | if (var->decl_node) { |
| 2156 | gen_var_debug_decl(g, var); |
| 2099 | 2157 | } |
| 2158 | fn_walk->data.inits.gen_i += 1; |
| 2159 | break; |
| 2160 | } |
| 2161 | return true; |
| 2162 | } else if (abi_class == X64CABIClass_INTEGER && ty_size <= 8) { |
| 2163 | switch (fn_walk->id) { |
| 2164 | case FnWalkIdAttrs: |
| 2165 | fn_walk->data.attrs.gen_i += 1; |
| 2166 | break; |
| 2167 | case FnWalkIdCall: { |
| 2168 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); |
| 2169 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); |
| 2170 | LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); |
| 2171 | fn_walk->data.call.gen_param_values->append(loaded); |
| 2172 | break; |
| 2100 | 2173 | } |
| 2101 | | if (contains_int) { |
| 2102 | | switch (fn_walk->id) { |
| 2103 | | case FnWalkIdAttrs: |
| 2104 | | fn_walk->data.attrs.gen_i += 1; |
| 2105 | | break; |
| 2106 | | case FnWalkIdCall: { |
| 2107 | | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); |
| 2108 | | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); |
| 2109 | | LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); |
| 2110 | | fn_walk->data.call.gen_param_values->append(loaded); |
| 2111 | | break; |
| 2112 | | } |
| 2113 | | case FnWalkIdTypes: { |
| 2114 | | ZigType *gen_type = get_int_type(g, false, ty_size * 8); |
| 2115 | | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); |
| 2116 | | fn_walk->data.types.param_di_types->append(gen_type->di_type); |
| 2117 | | break; |
| 2118 | | } |
| 2119 | | case FnWalkIdVars: { |
| 2120 | | di_arg_index = fn_walk->data.vars.gen_i; |
| 2121 | | var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); |
| 2122 | | fn_walk->data.vars.gen_i += 1; |
| 2123 | | dest_ty = ty; |
| 2124 | | goto var_ok; |
| 2125 | | } |
| 2126 | | case FnWalkIdInits: { |
| 2127 | | clear_debug_source_node(g); |
| 2128 | | LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); |
| 2129 | | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); |
| 2130 | | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); |
| 2131 | | gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); |
| 2132 | | if (var->decl_node) { |
| 2133 | | gen_var_debug_decl(g, var); |
| 2134 | | } |
| 2135 | | fn_walk->data.inits.gen_i += 1; |
| 2136 | | break; |
| 2137 | | } |
| 2174 | case FnWalkIdTypes: { |
| 2175 | ZigType *gen_type = get_int_type(g, false, ty_size * 8); |
| 2176 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); |
| 2177 | fn_walk->data.types.param_di_types->append(gen_type->di_type); |
| 2178 | break; |
| 2179 | } |
| 2180 | case FnWalkIdVars: { |
| 2181 | di_arg_index = fn_walk->data.vars.gen_i; |
| 2182 | var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); |
| 2183 | fn_walk->data.vars.gen_i += 1; |
| 2184 | dest_ty = ty; |
| 2185 | goto var_ok; |
| 2186 | } |
| 2187 | case FnWalkIdInits: { |
| 2188 | clear_debug_source_node(g); |
| 2189 | LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); |
| 2190 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); |
| 2191 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); |
| 2192 | gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); |
| 2193 | if (var->decl_node) { |
| 2194 | gen_var_debug_decl(g, var); |
| 2138 | 2195 | } |
| 2139 | | return true; |
| 2196 | fn_walk->data.inits.gen_i += 1; |
| 2197 | break; |
| 2140 | 2198 | } |
| 2141 | 2199 | } |
| 2200 | return true; |
| 2142 | 2201 | } |
| 2143 | 2202 | } |
| 2144 | 2203 | if (source_node != nullptr) { |