# HG changeset patch # User Leonard Ritter # Date 1556645676 -7200 # Tue Apr 30 19:34:36 2019 +0200 # Branch libtcc # Node ID 833b4b64f84f625d21927d2cb3bbb41424b504a4 # Parent d0b79f66249a857c1331719df6bc13f55f0d6008 * initial check-in gen_c diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -18,6 +18,8 @@ ^SPIRV-Tools ^SPIRV-Cross ^cparser/ +^tcc/ +^libjit/ ^libffi ^scopes$ ^testing/myscopes$ diff --git a/genie.lua b/genie.lua --- a/genie.lua +++ b/genie.lua @@ -138,6 +138,7 @@ "src/platform_abi.cpp", "src/gen_spirv.cpp", "src/gen_llvm.cpp", + "src/gen_c.cpp", "src/expander.cpp", "src/globals.cpp", "src/hash.cpp", diff --git a/include/scopes/scopes.h b/include/scopes/scopes.h --- a/include/scopes/scopes.h +++ b/include/scopes/scopes.h @@ -155,6 +155,7 @@ SCOPES_LIBEXPORT sc_valueref_raises_t sc_typify(const sc_closure_t *f, int numtypes, const sc_type_t **typeargs); SCOPES_LIBEXPORT sc_valueref_raises_t sc_typify_template(sc_valueref_t f, int numtypes, const sc_type_t **typeargs); SCOPES_LIBEXPORT sc_valueref_raises_t sc_compile(sc_valueref_t srcl, uint64_t flags); +SCOPES_LIBEXPORT sc_string_raises_t sc_compile_c(sc_scope_t *table, uint64_t flags); SCOPES_LIBEXPORT sc_string_raises_t sc_compile_spirv(sc_symbol_t target, sc_valueref_t srcl, uint64_t flags); SCOPES_LIBEXPORT sc_string_raises_t sc_compile_glsl(sc_symbol_t target, sc_valueref_t srcl, uint64_t flags); SCOPES_LIBEXPORT sc_void_raises_t sc_compile_object(const sc_string_t *path, sc_scope_t *table, uint64_t flags); diff --git a/lib/scopes/core.sc b/lib/scopes/core.sc --- a/lib/scopes/core.sc +++ b/lib/scopes/core.sc @@ -3913,14 +3913,17 @@ inline compile (func flags...) sc_compile func (parse-compile-flags flags...) + inline compile-c (table flags...) + sc_compile_c table (parse-compile-flags flags...) + inline compile-glsl (target func flags...) sc_compile_glsl target func (parse-compile-flags flags...) inline compile-spirv (target func flags...) sc_compile_spirv target func (parse-compile-flags flags...) - inline compile-object (func table flags...) - sc_compile_object func table (parse-compile-flags flags...) + inline compile-object (path table flags...) + sc_compile_object path table (parse-compile-flags flags...) inline convert-assert-args (args cond msg) if ((countof args) == 2) msg @@ -5060,8 +5063,8 @@ let _static-compile-spirv = (gen-static-compile-shader sc_compile_spirv) spice-quote - inline static-compile (func flags...) - _static-compile func (parse-compile-flags flags...) + inline static-compile-c (func flags...) + _static-compile-c func (parse-compile-flags flags...) inline static-compile-glsl (target func flags...) _static-compile-glsl target func (parse-compile-flags flags...) inline static-compile-spirv (target func flags...) diff --git a/src/gen_c.cpp b/src/gen_c.cpp new file mode 100644 --- /dev/null +++ b/src/gen_c.cpp @@ -0,0 +1,2218 @@ +/* + The Scopes Compiler Infrastructure + This file is distributed under the MIT License. + See LICENSE.md for details. +*/ + +#include "gen_c.hpp" +#include "source_file.hpp" +#include "types.hpp" +#include "anchor.hpp" +#include "error.hpp" +#include "execution.hpp" +#include "gc.hpp" +#include "scope.hpp" +#include "timer.hpp" +#include "value.hpp" +#include "stream_expr.hpp" +#include "compiler_flags.hpp" +#include "prover.hpp" +#include "hash.hpp" +#include "qualifiers.hpp" +#include "qualifier.inc" +#include "verify_tools.inc" + +#ifdef SCOPES_WIN32 +#include "stdlib_ex.h" +#else +#endif +#include + +#include "dyn_cast.inc" + +#include +#include + +#pragma GCC diagnostic ignored "-Wvla-extension" + +namespace scopes { + +#define SCOPES_GEN_TARGET "C" +#define SCOPES_LLVM_CACHE_FUNCTIONS 1 + +//------------------------------------------------------------------------------ +// IL->LLVM IR GENERATOR +//------------------------------------------------------------------------------ + +static const double deg2rad = 0.017453292519943295; +static const double rad2deg = 57.29577951308232; + +struct CGenerator { + enum Intrinsic { + c_sin_f32, + c_sin_f64, + c_cos_f32, + c_cos_f64, + c_sqrt_f32, + c_sqrt_f64, + c_fabs_f32, + c_fabs_f64, + c_trunc_f32, + c_trunc_f64, + c_floor_f32, + c_floor_f64, + c_pow_f32, + c_pow_f64, + c_exp_f32, + c_exp_f64, + c_log_f32, + c_log_f64, + c_exp2_f32, + c_exp2_f64, + c_log2_f32, + c_log2_f64, + custom_fsign_f32, + custom_fsign_f64, + custom_radians_f32, + custom_radians_f64, + custom_degrees_f32, + custom_degrees_f64, + NumIntrinsics, + }; + + typedef uint64_t Id; + typedef std::vector Ids; + + std::unordered_map ptr2global; + std::unordered_map ref2value; + std::unordered_map func2func; + std::unordered_map func_export_table; + std::unordered_map global2global; + std::deque function_todo; + static Types type_todo; + static Id next_id; + static std::unordered_map type_cache; + static std::unordered_map func_cache; + static std::unordered_map global_cache; + static std::unordered_map ref_cache; + static std::unordered_map definition_cache; + #define SCOPES_MAX_TEMP_SIZE 256 + static char temp[SCOPES_MAX_TEMP_SIZE]; + + static Id new_id() { + auto id = next_id++; + snprintf(temp, SCOPES_MAX_TEMP_SIZE, "_%ull", id); + ref_cache.insert({id, temp}); + return id; + } + + static Id new_id(const std::string &name) { + auto id = next_id++; + ref_cache.insert({id, name}); + return id; + } + + static Id define_type(Id id, std::string &definition) { + definition_cache.insert({id, definition}); + return id; + } + + static Id c_pointer_type(Id T) { + auto Tstr = ref_cache.at(T); + auto id = new_id(); + auto idstr = ref_cache.at(id); + snprintf(temp, SCOPES_MAX_TEMP_SIZE, "typedef %s *%s;", + ts.c_str(), idstr.c_str()); + define_type(id, temp); + return id; + } + + static Id c_int_type(int width) { + snprintf(temp, SCOPES_MAX_TEMP_SIZE, "int%i_t", width); + return new_id(temp); + } + + static Id c_real_type(int width) { + if (width == 32) { + return new_id("float"); + } else if (width == 64) { + return new_id("double"); + } else { + snprintf(temp, SCOPES_MAX_TEMP_SIZE, "real%i_t", width); + return new_id(temp); + } + } + + std::vector buffer_decl; + std::vector buffer_impl; + + static Id voidT; + static Id i1T; + static Id i8T; + static Id i16T; + static Id i32T; + static Id i64T; + static Id f32T; + static Id f32x2T; + static Id f64T; + static Id rawstringT; + static Id noneT; + static Id noneV; + static Id falseV; + static Id trueV; + static Id attr_byval; + static Id attr_sret; + static Id attr_nonnull; + Id intrinsics[NumIntrinsics]; + + bool use_debug_info; + bool generate_object; + FunctionRef active_function; + std::vector generated_symbols; + + static const Type *arguments_to_tuple(const Type *T) { + if (isa(T)) { + return cast(T)->to_tuple_type(); + } + return T; + } + + static const Type *abi_return_type(const FunctionType *ft) { + if (ft->has_exception()) { + if (is_returning_value(ft->except_type)) { + Types types = { TYPE_Bool, ft->except_type }; + if (is_returning_value(ft->return_type)) { + types.push_back(arguments_to_tuple(ft->return_type)); + } + return tuple_type(types).assert_ok(); + } else if (is_returning_value(ft->return_type)) { + Types types = { TYPE_Bool, ft->return_type }; + return tuple_type(types).assert_ok(); + } else { + return TYPE_Bool; + } + } else if (is_returning_value(ft->return_type)) { + return arguments_to_tuple(ft->return_type); + } else { + return empty_arguments_type(); + } + } + + struct TryInfo { + Id bb_except; + Ids except_values; + + TryInfo() : + bb_except(nullptr) + {} + + void clear() { + bb_except = nullptr; + except_values.clear(); + } + }; + + TryInfo try_info; + + struct LoopInfo { + LoopLabelRef loop; + Id bb_loop; + Ids repeat_values; + + LoopInfo() : + bb_loop(nullptr) + {} + }; + + LoopInfo loop_info; + + struct LabelInfo { + LabelRef label; + Id bb_merge; + Ids merge_values; + + LabelInfo() : + bb_merge(nullptr) + {} + }; + + std::vector label_info_stack; + + CGenerator() : + //active_function(nullptr), + //active_function_value(nullptr), + use_debug_info(true), + generate_object(false) { + static_init(); + for (int i = 0; i < NumIntrinsics; ++i) { + intrinsics[i] = 0; + } + } + + Id get_intrinsic(Intrinsic op) { + if (!intrinsics[op]) { + Id result = 0; + switch(op) { +#define C_INTRINSIC_IMPL(ENUMVAL, RETTYPE, STRNAME, ...) \ + case ENUMVAL: { \ + LLVMTypeRef argtypes[] = {__VA_ARGS__}; \ + result = LLVMAddFunction(module, STRNAME, LLVMFunctionType(RETTYPE, argtypes, sizeof(argtypes) / sizeof(LLVMTypeRef), false)); \ + } break; + +#define C_INTRINSIC_IMPL_BEGIN(ENUMVAL, RETTYPE, STRNAME, ...) \ + case ENUMVAL: { \ + LLVMTypeRef argtypes[] = { __VA_ARGS__ }; \ + result = LLVMAddFunction(module, STRNAME, \ + LLVMFunctionType(f32T, argtypes, sizeof(argtypes) / sizeof(LLVMTypeRef), false)); \ + LLVMSetLinkage(result, LLVMPrivateLinkage); \ + auto bb = LLVMAppendBasicBlock(result, ""); \ + auto oldbb = LLVMGetInsertBlock(builder); \ + position_builder_at_end(bb); + +#define C_INTRINSIC_IMPL_END() \ + position_builder_at_end(oldbb); \ + } break; + + C_INTRINSIC_IMPL(c_sin_f32, f32T, "llvm.sin.f32", f32T) + C_INTRINSIC_IMPL(c_sin_f64, f64T, "llvm.sin.f64", f64T) + C_INTRINSIC_IMPL(c_cos_f32, f32T, "llvm.cos.f32", f32T) + C_INTRINSIC_IMPL(c_cos_f64, f64T, "llvm.cos.f64", f64T) + + C_INTRINSIC_IMPL(c_sqrt_f32, f32T, "llvm.sqrt.f32", f32T) + C_INTRINSIC_IMPL(c_sqrt_f64, f64T, "llvm.sqrt.f64", f64T) + C_INTRINSIC_IMPL(c_fabs_f32, f32T, "llvm.fabs.f32", f32T) + C_INTRINSIC_IMPL(c_fabs_f64, f64T, "llvm.fabs.f64", f64T) + C_INTRINSIC_IMPL(c_trunc_f32, f32T, "llvm.trunc.f32", f32T) + C_INTRINSIC_IMPL(c_trunc_f64, f64T, "llvm.trunc.f64", f64T) + C_INTRINSIC_IMPL(c_floor_f32, f32T, "llvm.floor.f32", f32T) + C_INTRINSIC_IMPL(c_floor_f64, f64T, "llvm.floor.f64", f64T) + C_INTRINSIC_IMPL(c_pow_f32, f32T, "llvm.pow.f32", f32T, f32T) + C_INTRINSIC_IMPL(c_pow_f64, f64T, "llvm.pow.f64", f64T, f64T) + C_INTRINSIC_IMPL(c_exp_f32, f32T, "llvm.exp.f32", f32T) + C_INTRINSIC_IMPL(c_exp_f64, f64T, "llvm.exp.f64", f64T) + C_INTRINSIC_IMPL(c_log_f32, f32T, "llvm.log.f32", f32T) + C_INTRINSIC_IMPL(c_log_f64, f64T, "llvm.log.f64", f64T) + C_INTRINSIC_IMPL(c_exp2_f32, f32T, "llvm.exp2.f32", f32T) + C_INTRINSIC_IMPL(c_exp2_f64, f64T, "llvm.exp2.f64", f64T) + C_INTRINSIC_IMPL(c_log2_f32, f32T, "llvm.log2.f32", f32T) + C_INTRINSIC_IMPL(c_log2_f64, f64T, "llvm.log2.f64", f64T) + C_INTRINSIC_IMPL_BEGIN(custom_fsign_f32, f32T, "custom.fsign.f32", f32T) + // (0 < val) - (val < 0) + LLVMValueRef val = LLVMGetParam(result, 0); + LLVMValueRef zero = LLVMConstReal(f32T, 0.0); + LLVMValueRef a = LLVMBuildZExt(builder, LLVMBuildFCmp(builder, LLVMRealOLT, zero, val, ""), i8T, ""); + LLVMValueRef b = LLVMBuildZExt(builder, LLVMBuildFCmp(builder, LLVMRealOLT, val, zero, ""), i8T, ""); + val = LLVMBuildSub(builder, a, b, ""); + val = LLVMBuildSIToFP(builder, val, f32T, ""); + LLVMBuildRet(builder, val); + C_INTRINSIC_IMPL_END() + C_INTRINSIC_IMPL_BEGIN(custom_fsign_f64, f64T, "custom.fsign.f64", f64T) + // (0 < val) - (val < 0) + LLVMValueRef val = LLVMGetParam(result, 0); + LLVMValueRef zero = LLVMConstReal(f64T, 0.0); + LLVMValueRef a = LLVMBuildZExt(builder, LLVMBuildFCmp(builder, LLVMRealOLT, zero, val, ""), i8T, ""); + LLVMValueRef b = LLVMBuildZExt(builder, LLVMBuildFCmp(builder, LLVMRealOLT, val, zero, ""), i8T, ""); + val = LLVMBuildSub(builder, a, b, ""); + val = LLVMBuildSIToFP(builder, val, f64T, ""); + LLVMBuildRet(builder, val); + C_INTRINSIC_IMPL_END() + C_INTRINSIC_IMPL_BEGIN(custom_radians_f32, f32T, "custom.radians.f32", f32T) + LLVMBuildRet(builder, LLVMBuildFMul(builder, + LLVMGetParam(result, 0), LLVMConstReal(f32T, deg2rad), "")); + C_INTRINSIC_IMPL_END() + C_INTRINSIC_IMPL_BEGIN(custom_radians_f64, f64T, "custom.radians.f64", f64T) + LLVMBuildRet(builder, LLVMBuildFMul(builder, + LLVMGetParam(result, 0), LLVMConstReal(f64T, deg2rad), "")); + C_INTRINSIC_IMPL_END() + C_INTRINSIC_IMPL_BEGIN(custom_degrees_f32, f32T, "custom.degrees.f32", f32T) + LLVMBuildRet(builder, LLVMBuildFMul(builder, + LLVMGetParam(result, 0), LLVMConstReal(f32T, rad2deg), "")); + C_INTRINSIC_IMPL_END() + C_INTRINSIC_IMPL_BEGIN(custom_degrees_f64, f64T, "custom.degrees.f64", f64T) + LLVMBuildRet(builder, LLVMBuildFMul(builder, + LLVMGetParam(result, 0), LLVMConstReal(f64T, rad2deg), "")); + C_INTRINSIC_IMPL_END() +#undef C_INTRINSIC_IMPL +#undef C_INTRINSIC_IMPL_BEGIN +#undef C_INTRINSIC_IMPL_END + default: assert(false); break; + } + intrinsics[op] = result; + } + return intrinsics[op]; + } + + static void static_init() { + if (voidT) return; + voidT = LLVMVoidType(); + i1T = LLVMInt1Type(); + i8T = LLVMInt8Type(); + i16T = LLVMInt16Type(); + i32T = LLVMInt32Type(); + i64T = LLVMInt64Type(); + f32T = LLVMFloatType(); + f32x2T = LLVMVectorType(f32T, 2); + f64T = LLVMDoubleType(); + noneV = LLVMConstStruct(nullptr, 0, false); + noneT = LLVMTypeOf(noneV); + rawstringT = LLVMPointerType(LLVMInt8Type(), 0); + falseV = LLVMConstInt(i1T, 0, false); + trueV = LLVMConstInt(i1T, 1, false); + attr_byval = get_attribute("byval"); + attr_sret = get_attribute("sret"); + attr_nonnull = get_attribute("nonnull"); + + LLVMContextSetDiagnosticHandler(LLVMGetGlobalContext(), + diag_handler, + nullptr); + + } + + static SCOPES_RESULT(LLVMTypeRef) create_c_type(const Type *type) { + SCOPES_RESULT_TYPE(LLVMTypeRef); + switch(type->kind()) { + case TK_Qualify: { + auto qt = cast(type); + auto tid = SCOPES_GET_RESULT(_type_to_c_type(qt->type)); + auto rq = try_qualifier(type); + if (rq) { + tid = c_pointer_type(tid); + } + return tid; + } break; + case TK_Integer: + return c_int_type(cast(type)->width); + case TK_Real: + switch(cast(type)->width) { + case 32: return f32T; + case 64: return f64T; + default: break; + } + break; + case TK_Pointer: + return LLVMPointerType( + SCOPES_GET_RESULT(_type_to_llvm_type(cast(type)->element_type)), 0); + case TK_Array: { + auto ai = cast(type); + return LLVMArrayType(SCOPES_GET_RESULT(_type_to_llvm_type(ai->element_type)), ai->count); + } break; + case TK_Vector: { + auto vi = cast(type); + return LLVMVectorType(SCOPES_GET_RESULT(_type_to_llvm_type(vi->element_type)), vi->count); + } break; + case TK_Arguments: { + if (type == empty_arguments_type()) + return LLVMVoidType(); + return create_llvm_type(cast(type)->to_tuple_type()); + } break; + case TK_Tuple: { + auto ti = cast(type); + size_t count = ti->values.size(); + LLVMTypeRef elements[count]; + for (size_t i = 0; i < count; ++i) { + elements[i] = SCOPES_GET_RESULT(_type_to_llvm_type(ti->values[i])); + } + return LLVMStructType(elements, count, ti->packed); + } break; + case TK_Union: { + auto ui = cast(type); + return _type_to_llvm_type(ui->tuple_type); + } break; + case TK_Typename: { + if (type == TYPE_Sampler) { + SCOPES_ERROR(CGenTypeUnsupportedInTarget, TYPE_Sampler); + } + auto tn = cast(type); + if (!tn->is_opaque()) { + switch(tn->storage()->kind()) { + case TK_Tuple: + case TK_Union: { + type_todo.push_back(type); + } break; + default: { + return create_llvm_type(tn->storage()); + } break; + } + } + return LLVMStructCreateNamed( + LLVMGetGlobalContext(), tn->name()->data); + } break; + case TK_Function: { + auto fi = cast(type); + size_t count = fi->argument_types.size(); + auto rtype = abi_return_type(fi); + bool use_sret = is_memory_class(rtype); + + LLVMTypeRefs elements; + elements.reserve(count); + LLVMTypeRef rettype; + if (use_sret) { + elements.push_back( + LLVMPointerType(SCOPES_GET_RESULT(_type_to_llvm_type(rtype)), 0)); + rettype = voidT; + } else { + ABIClass classes[MAX_ABI_CLASSES]; + size_t sz = abi_classify(rtype, classes); + LLVMTypeRef T = SCOPES_GET_RESULT(_type_to_llvm_type(rtype)); + rettype = T; + if (sz) { + auto tk = LLVMGetTypeKind(T); + if (tk == LLVMStructTypeKind) { + auto ST = abi_struct_type(classes, sz); + if (ST) { rettype = ST; } + } + } + } + for (size_t i = 0; i < count; ++i) { + auto AT = fi->argument_types[i]; + SCOPES_CHECK_RESULT(abi_transform_parameter(AT, elements)); + } + return LLVMFunctionType(rettype, + &elements[0], elements.size(), fi->vararg()); + } break; + case TK_SampledImage: { + SCOPES_ERROR(CGenTypeUnsupportedInTarget, TYPE_SampledImage); + } break; + case TK_Image: { + SCOPES_ERROR(CGenTypeUnsupportedInTarget, TYPE_Image); + } break; + default: break; + }; + + SCOPES_ERROR(CGenFailedToTranslateType, type); + } + + static SCOPES_RESULT(size_t) finalize_types() { + SCOPES_RESULT_TYPE(size_t); + size_t result = type_todo.size(); + while (!type_todo.empty()) { + const Type *T = type_todo.back(); + type_todo.pop_back(); + auto tn = cast(T); + if (tn->is_opaque()) + continue; + LLVMTypeRef LLT = SCOPES_GET_RESULT(_type_to_llvm_type(T)); + const Type *ST = tn->storage(); + switch(ST->kind()) { + case TK_Tuple: { + auto ti = cast(ST); + size_t count = ti->values.size(); + LLVMTypeRef elements[count]; + for (size_t i = 0; i < count; ++i) { + elements[i] = SCOPES_GET_RESULT(_type_to_llvm_type(ti->values[i])); + } + LLVMStructSetBody(LLT, elements, count, false); + } break; + case TK_Union: { + auto ui = cast(ST); + size_t count = ui->values.size(); + size_t sz = ui->size; + size_t al = ui->align; + // find member with the same alignment + for (size_t i = 0; i < count; ++i) { + const Type *ET = ui->values[i]; + size_t etal = SCOPES_GET_RESULT(align_of(ET)); + if (etal == al) { + size_t remsz = sz - SCOPES_GET_RESULT(size_of(ET)); + LLVMTypeRef values[2]; + values[0] = SCOPES_GET_RESULT(_type_to_llvm_type(ET)); + if (remsz) { + // too small, add padding + values[1] = LLVMArrayType(i8T, remsz); + LLVMStructSetBody(LLT, values, 2, false); + } else { + LLVMStructSetBody(LLT, values, 1, false); + } + break; + } + } + } break; + default: assert(false); break; + } + } + return result; + } + + static SCOPES_RESULT(LLVMTypeRef) _type_to_llvm_type(const Type *type) { + SCOPES_RESULT_TYPE(LLVMTypeRef); + auto it = type_cache.find(type); + if (it == type_cache.end()) { + LLVMTypeRef result = SCOPES_GET_RESULT(create_llvm_type(type)); + type_cache.insert({type, result}); + return result; + } else { + return it->second; + } + } + + static SCOPES_RESULT(LLVMTypeRef) type_to_llvm_type(const Type *type) { + SCOPES_RESULT_TYPE(LLVMTypeRef); + auto typeref = SCOPES_GET_RESULT(_type_to_llvm_type(type)); + SCOPES_CHECK_RESULT(finalize_types()); + return typeref; + } + + static Error *last_llvm_error; + static void fatal_error_handler(const char *Reason) { + last_llvm_error = ErrorCGenBackendFailed::from(strdup(Reason)); + } + + void bind(const ValueIndex &node, LLVMValueRef value) { + assert(value); + ref2value.insert({node, value}); + } + + SCOPES_RESULT(LLVMValueRef) write_return(const LLVMValueRefs &values, bool is_except = false) { + SCOPES_RESULT_TYPE(LLVMValueRef); + assert(active_function); + auto fi = extract_function_type(active_function->get_type()); + auto rtype = abi_return_type(fi); + auto abiretT = SCOPES_GET_RESULT(type_to_llvm_type(rtype)); + LLVMValueRef value = nullptr; + if (fi->has_exception()) { + bool has_except_value = is_returning_value(fi->except_type); + bool has_return_value = is_returning_value(fi->return_type); + + if (has_except_value || has_return_value) { + auto abivalue = LLVMGetUndef(abiretT); + if (is_except) { + abivalue = LLVMBuildInsertValue(builder, abivalue, falseV, 0, ""); + if (is_returning_value(fi->except_type)) { + auto exceptT = SCOPES_GET_RESULT(type_to_llvm_type(fi->except_type)); + abivalue = LLVMBuildInsertValue(builder, abivalue, + values_to_struct(exceptT, values), 1, ""); + } + } else { + abivalue = LLVMBuildInsertValue(builder, abivalue, trueV, 0, ""); + if (is_returning_value(fi->return_type)) { + int retvalueindex = (has_except_value?2:1); + auto returnT = SCOPES_GET_RESULT(type_to_llvm_type(fi->return_type)); + abivalue = LLVMBuildInsertValue(builder, abivalue, + values_to_struct(returnT, values), retvalueindex, ""); + } + } + value = abivalue; + } else { + if (is_except) { + value = falseV; + } else { + value = trueV; + } + } + } else { + if (is_returning_value(fi->return_type)) { + auto returnT = SCOPES_GET_RESULT(type_to_llvm_type(fi->return_type)); + value = values_to_struct(returnT, values); + } + } + LLVMValueRef parentfunc = LLVMGetBasicBlockParent(LLVMGetInsertBlock(builder)); + bool use_sret = is_memory_class(rtype); + if (use_sret) { + LLVMBuildStore(builder, value, LLVMGetParam(parentfunc, 0)); + return LLVMBuildRetVoid(builder); + } else if (rtype == empty_arguments_type()) { + return LLVMBuildRetVoid(builder); + } else { + assert(value); + // check if ABI needs something else and do a bitcast + auto retT = LLVMGetReturnType(LLVMGetElementType(LLVMTypeOf(parentfunc))); + auto srcT = abiretT; + if (retT != srcT) { + LLVMValueRef dest = safe_alloca(srcT); + LLVMBuildStore(builder, value, dest); + value = LLVMBuildBitCast(builder, dest, LLVMPointerType(retT, 0), ""); + value = LLVMBuildLoad(builder, value, ""); + } + return LLVMBuildRet(builder, value); + } + } + + SCOPES_RESULT(LLVMValueRef) write_return(const TypedValues &values, bool is_except = false) { + SCOPES_RESULT_TYPE(LLVMValueRef); + LLVMValueRefs refs; + for (auto val : values) { + refs.push_back(SCOPES_GET_RESULT(ref_to_value(val))); + } + return write_return(refs, is_except); + } + + SCOPES_RESULT(void) translate_Return(const ReturnRef &node) { + SCOPES_RESULT_TYPE(void); + SCOPES_CHECK_RESULT(write_return(node->values)); + return {}; + } + + SCOPES_RESULT(void) translate_Raise(const RaiseRef &node) { + SCOPES_RESULT_TYPE(void); + SCOPES_CHECK_RESULT(build_merge_phi(try_info.except_values, node->values)); + LLVMBuildBr(builder, try_info.bb_except); + return {}; + } + + SCOPES_RESULT(void) Function_finalize(const FunctionRef &node) { + SCOPES_RESULT_TYPE(void); + + active_function = node; + auto it = ref2value.find(ValueIndex(node)); + assert(it != ref2value.end()); + LLVMValueRef func = it->second; + assert(func); + auto ilfunctype = node->get_type(); + auto fi = extract_function_type(ilfunctype); + auto rtype = abi_return_type(fi); + bool use_sret = is_memory_class(rtype); + auto bb = LLVMAppendBasicBlock(func, ""); + + try_info.clear(); + + if (!node->raises.empty()) { + try_info.bb_except = LLVMAppendBasicBlock(func, "except"); + position_builder_at_end(try_info.bb_except); + if (use_debug_info) + set_debug_location(node.anchor()); + SCOPES_CHECK_RESULT(build_phi(try_info.except_values, fi->except_type)); + SCOPES_CHECK_RESULT(write_return(try_info.except_values, true)); + } + + position_builder_at_end(bb); + + auto &¶ms = node->params; + size_t offset = 0; + if (use_sret) { + offset++; + //Parameter *param = params[0]; + //bind(param, LLVMGetParam(func, 0)); + } + + size_t paramcount = params.size(); + + if (use_debug_info) + set_debug_location(node.anchor()); + size_t k = offset; + for (size_t i = 0; i < paramcount; ++i) { + ParameterRef param = params[i]; + LLVMValueRef val = SCOPES_GET_RESULT(abi_import_argument(param->get_type(), func, k)); + assert(val); + bind(ValueIndex(param), val); + } + SCOPES_CHECK_RESULT(translate_block(node->body)); + return {}; + } + + SCOPES_RESULT(LLVMValueRef) Function_to_value(const FunctionRef &node) { + SCOPES_RESULT_TYPE(LLVMValueRef); + + auto ilfunctype = extract_function_type(node->get_type()); + + bool is_export = false; + const String *name = nullptr; + { + auto it = func_export_table.find(node.unref()); + if (it != func_export_table.end()) { + name = it->second.name(); + is_export = true; + } + } + if (!name) { + auto funcname = node->name; + StyledString ss = StyledString::plain(); + if (funcname == SYM_Unnamed) { + ss.out << "unnamed"; + } else { + ss.out << funcname.name()->data; + } + + ss.out << "<"; + int index = 0; + for (auto T : ilfunctype->argument_types) { + if (index > 0) + ss.out << ","; + stream_type_name(ss.out, T); + index++; + } + ss.out << ">"; + stream_address(ss.out, node.unref()); + name = ss.str(); + } + + auto functype = SCOPES_GET_RESULT(type_to_llvm_type(ilfunctype)); + + auto func = LLVMAddFunction(module, name->data, functype); + +#if SCOPES_LLVM_CACHE_FUNCTIONS + if (!generate_object) { + auto it = func_cache.find(node.unref()); + if (it != func_cache.end()) { + assert(it->second != module); + return func; + } + + func_cache.insert({node.unref(), module}); + } +#endif + generated_symbols.push_back(func); + + if (use_debug_info) { + LLVMSetSubprogram(func, function_to_subprogram(node)); + } + if (is_export) { + LLVMSetLinkage(func, LLVMExternalLinkage); + } else if (generate_object) { + LLVMSetLinkage(func, LLVMPrivateLinkage); + } else { +#if !SCOPES_LLVM_CACHE_FUNCTIONS + LLVMSetLinkage(func, LLVMPrivateLinkage); +#endif + } + function_todo.push_back(node); + return func; + } + + SCOPES_RESULT(LLVMValueRef) Parameter_to_value(const ParameterRef &node) { + SCOPES_RESULT_TYPE(LLVMValueRef); + SCOPES_ERROR(CGenUnboundValue, node); + } + + SCOPES_RESULT(LLVMValueRef) LoopLabelArguments_to_value(const LoopLabelArgumentsRef &node) { + SCOPES_RESULT_TYPE(LLVMValueRef); + SCOPES_ERROR(CGenUnboundValue, node); + } + + SCOPES_RESULT(LLVMValueRef) Exception_to_value(const ExceptionRef &node) { + SCOPES_RESULT_TYPE(LLVMValueRef); + SCOPES_ERROR(CGenUnboundValue, node); + } + + SCOPES_RESULT(void) translate_block(const Block &node) { + SCOPES_RESULT_TYPE(void); + for (auto entry : node.body) { + SCOPES_CHECK_RESULT(translate_instruction(entry)); + } + if (node.terminator) { + SCOPES_CHECK_RESULT(translate_instruction(node.terminator)); + } + return {}; + } + + + LabelInfo &find_label_info(const LabelRef &label) { + int i = label_info_stack.size(); + while (i-- > 0) { + auto &&info = label_info_stack[i]; + if (info.label == label) + return info; + } + assert(false); + return label_info_stack.back(); + } + + SCOPES_RESULT(void) build_merge_phi(const LLVMValueRefs &phis, const TypedValues &values) { + SCOPES_RESULT_TYPE(void); + if (phis.size() != values.size()) { + StyledStream ss; + for (auto phi : phis) { + LLVMDumpValue(phi); + ss << std::endl; + } + for (auto value : values) { + stream_value(ss, value, StreamValueFormat::singleline()); + } + } + assert(phis.size() == values.size()); + LLVMBasicBlockRef bb = LLVMGetInsertBlock(builder); + assert(bb); + LLVMBasicBlockRef incobbs[] = { bb }; + int count = phis.size(); + for (int i = 0; i < count; ++i) { + auto phi = phis[i]; + auto llvmval = SCOPES_GET_RESULT(ref_to_value(values[i])); + LLVMValueRef incovals[] = { llvmval }; + LLVMAddIncoming(phi, incovals, incobbs, 1); + } + return {}; + } + + SCOPES_RESULT(LLVMValueRef) build_merge(const LabelRef &label, const TypedValues &values) { + SCOPES_RESULT_TYPE(LLVMValueRef); + auto &&label_info = find_label_info(label); + SCOPES_CHECK_RESULT(build_merge_phi(label_info.merge_values, values)); + assert(label_info.bb_merge); + return LLVMBuildBr(builder, label_info.bb_merge); + } + + SCOPES_RESULT(void) translate_Merge(const MergeRef &node) { + SCOPES_RESULT_TYPE(void); + auto label = node->label.cast