| ... | @@ -1894,6 +1894,91 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { | ... | @@ -1894,6 +1894,91 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { |
| 1894 | report_errors_and_exit(g); | 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 | static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { | 1982 | static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { |
| 1898 | size_t ty_size = type_size(g, ty); | 1983 | size_t ty_size = type_size(g, ty); |
| 1899 | if (ty_size > g->pointer_size_bytes) | 1984 | if (ty_size > g->pointer_size_bytes) |
| ... | @@ -1905,13 +1990,6 @@ static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { | ... | @@ -1905,13 +1990,6 @@ static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { |
| 1905 | get_codegen_ptr_type(ty) != nullptr); | 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 | static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { | 1993 | static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { |
| 1916 | // Initialized from the type for some walks, but because of C var args, | 1994 | // Initialized from the type for some walks, but because of C var args, |
| 1917 | // initialized based on callsite instructions for that one. | 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,98 +2125,79 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ |
| 2047 | } | 2125 | } |
| 2048 | | 2126 | |
| 2049 | if (g->zig_target.arch.arch == ZigLLVM_x86_64) { | 2127 | if (g->zig_target.arch.arch == ZigLLVM_x86_64) { |
| | 2128 | X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); |
| 2050 | size_t ty_size = type_size(g, ty); | 2129 | size_t ty_size = type_size(g, ty); |
| 2051 | if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { | 2130 | if (abi_class == X64CABIClass_MEMORY) { |
| 2052 | assert(handle_is_ptr(ty)); | 2131 | assert(handle_is_ptr(ty)); |
| 2053 | | 2132 | switch (fn_walk->id) { |
| 2054 | // "If the size of an object is larger than four eightbytes, or it contains unaligned | 2133 | case FnWalkIdAttrs: |
| 2055 | // fields, it has class MEMORY" | 2134 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); |
| 2056 | if (ty_size > 32) { | 2135 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); |
| 2057 | switch (fn_walk->id) { | 2136 | fn_walk->data.attrs.gen_i += 1; |
| 2058 | case FnWalkIdAttrs: | 2137 | break; |
| 2059 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); | 2138 | case FnWalkIdCall: |
| 2060 | addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); | 2139 | fn_walk->data.call.gen_param_values->append(val); |
| 2061 | fn_walk->data.attrs.gen_i += 1; | 2140 | break; |
| 2062 | break; | 2141 | case FnWalkIdTypes: { |
| 2063 | case FnWalkIdCall: | 2142 | ZigType *gen_type = get_pointer_to_type(g, ty, true); |
| 2064 | fn_walk->data.call.gen_param_values->append(val); | 2143 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); |
| 2065 | break; | 2144 | fn_walk->data.types.param_di_types->append(gen_type->di_type); |
| 2066 | case FnWalkIdTypes: { | 2145 | break; |
| 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; | | |
| 2085 | } | 2146 | } |
| 2086 | return true; | 2147 | case FnWalkIdVars: { |
| 2087 | } | 2148 | di_arg_index = fn_walk->data.vars.gen_i; |
| 2088 | } | 2149 | var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); |
| 2089 | if (ty->id == ZigTypeIdStruct) { | 2150 | dest_ty = get_pointer_to_type(g, ty, false); |
| 2090 | assert(handle_is_ptr(ty)); | 2151 | fn_walk->data.vars.gen_i += 1; |
| 2091 | // "If the size of the aggregate exceeds a single eightbyte, each is classified | 2152 | goto var_ok; |
| 2092 | // separately. Each eightbyte gets initialized to class NO_CLASS." | 2153 | } |
| 2093 | if (ty_size <= 8) { | 2154 | case FnWalkIdInits: |
| 2094 | bool contains_int = false; | 2155 | if (var->decl_node) { |
| 2095 | for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { | 2156 | gen_var_debug_decl(g, var); |
| 2096 | if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { | | |
| 2097 | contains_int = true; | | |
| 2098 | break; | | |
| 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) { | 2174 | case FnWalkIdTypes: { |
| 2102 | switch (fn_walk->id) { | 2175 | ZigType *gen_type = get_int_type(g, false, ty_size * 8); |
| 2103 | case FnWalkIdAttrs: | 2176 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); |
| 2104 | fn_walk->data.attrs.gen_i += 1; | 2177 | fn_walk->data.types.param_di_types->append(gen_type->di_type); |
| 2105 | break; | 2178 | break; |
| 2106 | case FnWalkIdCall: { | 2179 | } |
| 2107 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); | 2180 | case FnWalkIdVars: { |
| 2108 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); | 2181 | di_arg_index = fn_walk->data.vars.gen_i; |
| 2109 | LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); | 2182 | var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); |
| 2110 | fn_walk->data.call.gen_param_values->append(loaded); | 2183 | fn_walk->data.vars.gen_i += 1; |
| 2111 | break; | 2184 | dest_ty = ty; |
| 2112 | } | 2185 | goto var_ok; |
| 2113 | case FnWalkIdTypes: { | 2186 | } |
| 2114 | ZigType *gen_type = get_int_type(g, false, ty_size * 8); | 2187 | case FnWalkIdInits: { |
| 2115 | fn_walk->data.types.gen_param_types->append(gen_type->type_ref); | 2188 | clear_debug_source_node(g); |
| 2116 | fn_walk->data.types.param_di_types->append(gen_type->di_type); | 2189 | LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); |
| 2117 | break; | 2190 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); |
| 2118 | } | 2191 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); |
| 2119 | case FnWalkIdVars: { | 2192 | gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); |
| 2120 | di_arg_index = fn_walk->data.vars.gen_i; | 2193 | if (var->decl_node) { |
| 2121 | var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); | 2194 | gen_var_debug_decl(g, var); |
| 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 | } | | |
| 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 | if (source_node != nullptr) { | 2203 | if (source_node != nullptr) { |