/* * MRustC - Rust Compiler * - By John Hodge (Mutabah/thePowersGang) * * trans/codegen_c.cpp * - Code generation emitting C code */ #include "codegen.hpp" #include "mangling.hpp" #include #include #include #include #include #include #include namespace { class CodeGenerator_C: public CodeGenerator { enum class MetadataType { None, Slice, TraitObject, }; static Span sp; const ::HIR::Crate& m_crate; ::StaticTraitResolve m_resolve; ::std::string m_outfile_path; ::std::string m_outfile_path_c; ::std::ofstream m_of; const ::MIR::TypeResolve* m_mir_res; ::std::map<::HIR::GenericPath, ::std::vector> m_enum_repr_cache; ::std::vector< ::std::pair< ::HIR::GenericPath, const ::HIR::Struct*> > m_box_glue_todo; public: CodeGenerator_C(const ::HIR::Crate& crate, const ::std::string& outfile): m_crate(crate), m_resolve(crate), m_outfile_path(outfile), m_outfile_path_c(outfile + ".c"), m_of(m_outfile_path_c) { m_of << "/*\n" << " * AUTOGENERATED by mrustc\n" << " */\n" << "#include \n" << "#include \n" << "#include \n" << "#include \n" // atomic_* << "#include \n" // abort << "#include \n" // mem* << "#include \n" // round, ... << "typedef uint32_t CHAR;\n" << "typedef struct { } tUNIT;\n" << "typedef struct { } tBANG;\n" << "typedef struct { } tTYPEID;\n" << "typedef struct { void* PTR; size_t META; } SLICE_PTR;\n" << "typedef struct { void* PTR; void* META; } TRAITOBJ_PTR;\n" << "typedef struct { size_t size; size_t align; } VTABLE_HDR;\n" << "\n" << "extern void _Unwind_Resume(void) __attribute__((noreturn));\n" << "\n" << "static inline int slice_cmp(SLICE_PTR l, SLICE_PTR r) {\n" << "\tint rv = memcmp(l.PTR, r.PTR, l.META < r.META ? l.META : r.META);\n" << "\tif(rv != 0) return rv;\n" << "\tif(l.META < r.META) return -1;\n" << "\tif(l.META > r.META) return 1;\n" << "\treturn 0;\n" << "}\n" << "static inline SLICE_PTR make_sliceptr(void* ptr, size_t s) { SLICE_PTR rv = { ptr, s }; return rv; }\n" << "static inline TRAITOBJ_PTR make_traitobjptr(void* ptr, void* vt) { TRAITOBJ_PTR rv = { ptr, vt }; return rv; }\n" << "\n" << "static inline size_t max(size_t a, size_t b) { return a < b ? b : a; }\n" << "static inline uint64_t __builtin_clz64(uint64_t v) {\n" << "\treturn (v >> 32 != 0 ? __builtin_clz(v>>32) : 32 + __builtin_clz(v));\n" << "}\n" << "static inline uint64_t __builtin_ctz64(uint64_t v) {\n" << "\treturn (v&0xFFFFFFFF == 0 ? __builtin_ctz(v>>32) + 32 : __builtin_ctz(v));\n" << "}\n" << "static inline unsigned __int128 __builtin_bswap128(unsigned __int128 v) {\n" << "\tuint64_t lo = __builtin_bswap64((uint64_t)v);\n" << "\tuint64_t hi = __builtin_bswap64((uint64_t)(v>>64));\n" << "\treturn ((unsigned __int128)lo << 64) | (unsigned __int128)hi;\n" << "}\n" << "static inline unsigned __int128 __builtin_clz128(unsigned __int128 v) {\n" << "\treturn (v >> 64 != 0 ? __builtin_clz64(v>>64) : 64 + __builtin_clz64(v));\n" << "}\n" << "static inline unsigned __int128 __builtin_ctz128(unsigned __int128 v) {\n" << "\treturn (v&0xFFFFFFFFFFFFFFFF == 0 ? __builtin_ctz64(v>>64) + 64 : __builtin_ctz64(v));\n" << "}\n" << "\n" ; } ~CodeGenerator_C() {} void finalise(bool is_executable, const TransOptions& opt) override { // Emit box drop glue after everything else to avoid definition ordering issues for(auto& e : m_box_glue_todo) { emit_box_drop_glue( mv$(e.first), *e.second ); } if( is_executable ) { m_of << "int main(int argc, const char* argv[]) {\n"; auto c_start_path = m_resolve.m_crate.get_lang_item_path_opt("mrustc-start"); if( c_start_path == ::HIR::SimplePath() ) { m_of << "\t" << Trans_Mangle( ::HIR::GenericPath(m_resolve.m_crate.get_lang_item_path(Span(), "start")) ) << "(" << "(uint8_t*)" << Trans_Mangle( ::HIR::GenericPath(m_resolve.m_crate.get_lang_item_path(Span(), "mrustc-main")) ) << ", argc, (uint8_t**)argv" << ");\n"; } else { m_of << "\t" << Trans_Mangle(::HIR::GenericPath(c_start_path)) << "(argc, argv);\n"; } m_of << "}\n"; } m_of.flush(); // Execute $CC with the required libraries ::std::vector<::std::string> tmp; ::std::vector args; args.push_back( getenv("CC") ? getenv("CC") : "gcc" ); args.push_back("-pthread"); switch(opt.opt_level) { case 0: break; case 1: args.push_back("-O1"); break; case 2: args.push_back("-O2"); break; } if( opt.emit_debug_info ) { args.push_back("-g"); } args.push_back("-o"); args.push_back(m_outfile_path.c_str()); args.push_back(m_outfile_path_c.c_str()); if( is_executable ) { for( const auto& crate : m_crate.m_ext_crates ) { tmp.push_back(crate.second.m_filename + ".o"); args.push_back(tmp.back().c_str()); } for(const auto& path : opt.library_search_dirs ) { args.push_back("-L"); args.push_back(path.c_str()); } for(const auto& lib : m_crate.m_ext_libs) { ASSERT_BUG(Span(), lib.name != "", ""); args.push_back("-l"); args.push_back(lib.name.c_str()); } for( const auto& crate : m_crate.m_ext_crates ) { for(const auto& lib : crate.second.m_data->m_ext_libs) { ASSERT_BUG(Span(), lib.name != "", "Empty lib from " << crate.first); args.push_back("-l"); args.push_back(lib.name.c_str()); } } for(const auto& path : opt.libraries ) { args.push_back("-l"); args.push_back(path.c_str()); } args.push_back("-z"); args.push_back("muldefs"); args.push_back("-Wl,--gc-sections"); } else { args.push_back("-c"); } ::std::stringstream cmd_ss; for(const auto& arg : args) { cmd_ss << "\"" << FmtEscaped(arg) << "\" "; } DEBUG("- " << cmd_ss.str()); if( system(cmd_ss.str().c_str()) ) { abort(); } } void emit_box_drop_glue(::HIR::GenericPath p, const ::HIR::Struct& item) { auto struct_ty = ::HIR::TypeRef( p.clone(), &item ); auto drop_glue_path = ::HIR::Path(struct_ty.clone(), "#drop_glue"); auto struct_ty_ptr = ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Owned, struct_ty.clone()); // - Drop Glue const auto* ity = m_resolve.is_type_owned_box(struct_ty); auto inner_ptr = ::HIR::TypeRef::new_pointer( ::HIR::BorrowType::Unique, ity->clone() ); auto box_free = ::HIR::GenericPath { m_crate.get_lang_item_path(sp, "box_free"), { ity->clone() } }; ::std::vector< ::std::pair<::HIR::Pattern,::HIR::TypeRef> > args; args.push_back( ::std::make_pair( ::HIR::Pattern {}, mv$(inner_ptr) ) ); ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), struct_ty_ptr, args, *(::MIR::Function*)nullptr }; m_mir_res = &mir_res; m_of << "void " << Trans_Mangle(drop_glue_path) << "(struct s_" << Trans_Mangle(p) << "* rv) {\n"; // Obtain inner pointer // TODO: This is very specific to the structure of the official liballoc's Box. m_of << "\t"; emit_ctype(args[0].second, FMT_CB(ss, ss << "arg0"; )); m_of << " = rv->_0._0._0;\n"; // Call destructor of inner data emit_destructor_call( ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({0})) }), *ity, true); // Emit a call to box_free for the type m_of << "\t" << Trans_Mangle(box_free) << "(arg0);\n"; m_of << "}\n"; m_mir_res = nullptr; } void emit_type_id(const ::HIR::TypeRef& ty) override { m_of << "tTYPEID __typeid_" << Trans_Mangle(ty) << " __attribute__((weak));\n"; } void emit_type_proto(const ::HIR::TypeRef& ty) override { TRACE_FUNCTION_F(ty); TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Tuple, te, if( te.size() > 0 ) { m_of << "typedef struct "; emit_ctype(ty); m_of << " "; emit_ctype(ty); m_of << ";\n"; } ) else TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Function, te, emit_type_fn(ty); m_of << "\n"; ) else TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Array, te, m_of << "typedef struct "; emit_ctype(ty); m_of << " "; emit_ctype(ty); m_of << ";\n"; ) else TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Path, te, TU_MATCHA( (te.binding), (tpb), (Unbound, throw ""; ), (Opaque, throw ""; ), (Struct, m_of << "struct s_" << Trans_Mangle(te.path) << ";\n"; ), (Union, m_of << "union u_" << Trans_Mangle(te.path) << ";\n"; ), (Enum, m_of << "struct e_" << Trans_Mangle(te.path) << ";\n"; ) ) ) else if( ty.m_data.is_ErasedType() ) { // TODO: Is this actually a bug? return ; } else { } } void emit_type_fn(const ::HIR::TypeRef& ty) { const auto& te = ty.m_data.as_Function(); m_of << "typedef "; // TODO: ABI marker, need an ABI enum? // TODO: Better emit_ctype call for return type. emit_ctype(*te.m_rettype); m_of << " (*"; emit_ctype(ty); m_of << ")("; if( te.m_arg_types.size() == 0 ) { m_of << "void)"; } else { for(unsigned int i = 0; i < te.m_arg_types.size(); i ++) { if(i != 0) m_of << ","; m_of << " "; emit_ctype(te.m_arg_types[i]); } m_of << " )"; } m_of << ";"; } void emit_type(const ::HIR::TypeRef& ty) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "type " << ty;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(ty); TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Tuple, te, if( te.size() > 0 ) { m_of << "typedef struct "; emit_ctype(ty); m_of << " {\n"; for(unsigned int i = 0; i < te.size(); i++) { m_of << "\t"; emit_ctype(te[i], FMT_CB(ss, ss << "_" << i;)); m_of << ";\n"; } m_of << "} "; emit_ctype(ty); m_of << ";\n"; } ) else TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Function, te, emit_type_fn(ty); m_of << " // " << ty << "\n"; ) else TU_IFLET( ::HIR::TypeRef::Data, ty.m_data, Array, te, m_of << "typedef struct "; emit_ctype(ty); m_of << " { "; emit_ctype(*te.inner); m_of << " DATA[" << te.size_val << "]; } "; emit_ctype(ty); m_of << ";\n"; ) else if( ty.m_data.is_ErasedType() ) { // TODO: Is this actually a bug? return ; } else { } m_mir_res = nullptr; } void emit_struct(const Span& sp, const ::HIR::GenericPath& p, const ::HIR::Struct& item) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "struct " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); ::HIR::TypeRef tmp; auto monomorph = [&](const auto& x)->const auto& { if( monomorphise_type_needed(x) ) { tmp = monomorphise_type(sp, item.m_params, p.m_params, x); m_resolve.expand_associated_types(sp, tmp); return tmp; } else { return x; } }; bool has_unsized = false; auto emit_struct_fld_ty = [&](const ::HIR::TypeRef& ty_raw, ::FmtLambda inner) { const auto& ty = monomorph(ty_raw); TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Slice, te, emit_ctype( *te.inner, FMT_CB(ss, ss << inner << "[0]";) ); has_unsized = true; ) else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, TraitObject, te, m_of << "unsigned char " << inner << "[0]"; has_unsized = true; ) else if( ty == ::HIR::CoreType::Str ) { m_of << "uint8_t " << inner << "[0]"; has_unsized = true; } else { emit_ctype( ty, inner ); } }; m_of << "// struct " << p << "\n"; m_of << "struct s_" << Trans_Mangle(p) << " {\n"; // HACK: For vtables, insert the alignment and size at the start { const auto& lc = p.m_path.m_components.back(); if( lc.size() > 7 && ::std::strcmp(lc.c_str() + lc.size() - 7, "#vtable") == 0 ) { m_of << "\tVTABLE_HDR hdr;\n"; } } TU_MATCHA( (item.m_data), (e), (Unit, ), (Tuple, for(unsigned int i = 0; i < e.size(); i ++) { const auto& fld = e[i]; m_of << "\t"; emit_struct_fld_ty(fld.ent, FMT_CB(ss, ss << "_" << i;)); m_of << ";\n"; } ), (Named, for(unsigned int i = 0; i < e.size(); i ++) { const auto& fld = e[i].second; m_of << "\t"; emit_struct_fld_ty(fld.ent, FMT_CB(ss, ss << "_" << i;)); m_of << ";\n"; } ) ) m_of << "};\n"; // Crate constructor function if( !has_unsized ) { TU_IFLET(::HIR::Struct::Data, item.m_data, Tuple, e, m_of << "struct s_" << Trans_Mangle(p) << " " << Trans_Mangle(p) << "("; for(unsigned int i = 0; i < e.size(); i ++) { if(i != 0) m_of << ", "; emit_ctype( monomorph(e[i].ent), FMT_CB(ss, ss << "_" << i;) ); } m_of << ") {\n"; m_of << "\tstruct s_" << Trans_Mangle(p) << " rv = {"; for(unsigned int i = 0; i < e.size(); i ++) { if(i != 0) m_of << ","; m_of << "\n\t\t_" << i; } m_of << "\n\t\t};\n"; m_of << "\treturn rv;\n"; m_of << "}\n"; ) } auto struct_ty = ::HIR::TypeRef(p.clone(), &item); auto drop_glue_path = ::HIR::Path(struct_ty.clone(), "#drop_glue"); auto struct_ty_ptr = ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Owned, struct_ty.clone()); // - Drop Glue ::std::vector< ::std::pair<::HIR::Pattern,::HIR::TypeRef> > args; if( item.m_markings.has_drop_impl ) { m_of << "tUNIT " << Trans_Mangle( ::HIR::Path(struct_ty.clone(), m_resolve.m_lang_Drop, "drop") ) << "(struct s_" << Trans_Mangle(p) << "*rv);\n"; } else if( m_resolve.is_type_owned_box(struct_ty) ) { m_box_glue_todo.push_back( ::std::make_pair( mv$(struct_ty.m_data.as_Path().path.m_data.as_Generic()), &item ) ); m_of << "void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(struct_ty_ptr, FMT_CB(ss, ss << "rv";)); m_of << ");\n"; return ; } ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), struct_ty_ptr, args, *(::MIR::Function*)nullptr }; m_mir_res = &mir_res; m_of << "void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(struct_ty_ptr, FMT_CB(ss, ss << "rv";)); m_of << ") {\n"; // If this type has an impl of Drop, call that impl if( item.m_markings.has_drop_impl ) { m_of << "\t" << Trans_Mangle( ::HIR::Path(struct_ty.clone(), m_resolve.m_lang_Drop, "drop") ) << "(rv);\n"; } auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); TU_MATCHA( (item.m_data), (e), (Unit, ), (Tuple, for(unsigned int i = 0; i < e.size(); i ++) { const auto& fld = e[i]; fld_lv.as_Field().field_index = i; emit_destructor_call(fld_lv, monomorph(fld.ent), true); } ), (Named, for(unsigned int i = 0; i < e.size(); i ++) { const auto& fld = e[i].second; fld_lv.as_Field().field_index = i; emit_destructor_call(fld_lv, monomorph(fld.ent), true); } ) ) m_of << "}\n"; m_mir_res = nullptr; } void emit_union(const Span& sp, const ::HIR::GenericPath& p, const ::HIR::Union& item) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "union " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); ::HIR::TypeRef tmp; auto monomorph = [&](const auto& x)->const auto& { if( monomorphise_type_needed(x) ) { tmp = monomorphise_type(sp, item.m_params, p.m_params, x); m_resolve.expand_associated_types(sp, tmp); return tmp; } else { return x; } }; m_of << "union u_" << Trans_Mangle(p) << " {\n"; for(unsigned int i = 0; i < item.m_variants.size(); i ++) { m_of << "\t"; emit_ctype( monomorph(item.m_variants[i].second.ent), FMT_CB(ss, ss << "var_" << i;) ); m_of << ";\n"; } m_of << "};\n"; // Drop glue (calls destructor if there is one) auto item_ty = ::HIR::TypeRef(p.clone(), &item); auto drop_glue_path = ::HIR::Path(item_ty.clone(), "#drop_glue"); auto item_ptr_ty = ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Owned, item_ty.clone()); auto drop_impl_path = (item.m_markings.has_drop_impl ? ::HIR::Path(item_ty.clone(), m_resolve.m_lang_Drop, "drop") : ::HIR::Path(::HIR::SimplePath())); ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), item_ptr_ty, {}, *(::MIR::Function*)nullptr }; m_mir_res = &mir_res; if( item.m_markings.has_drop_impl ) { m_of << "tUNIT " << Trans_Mangle(drop_impl_path) << "(union u_" << Trans_Mangle(p) << "*rv);\n"; } m_of << "void " << Trans_Mangle(drop_glue_path) << "(union u_" << Trans_Mangle(p) << "* rv) {\n"; if( item.m_markings.has_drop_impl ) { m_of << "\t" << Trans_Mangle(drop_impl_path) << "(rv);\n"; } m_of << "}\n"; } // TODO: Move this to codegen.cpp? bool get_nonzero_path(const Span& sp, const ::HIR::TypeRef& ty, ::std::vector& out) const { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( return false; ), (Borrow, if( metadata_type(*te.inner) != MetadataType::None ) { // HACK: If the inner is a DST, emit ~0 as a marker out.push_back(~0u); } return true; ), (Function, return true; ) ) } void emit_nonzero_path(const ::std::vector& nonzero_path) { for(const auto v : nonzero_path) { if(v == ~0u) { // NOTE: Should only ever be the last m_of << ".PTR"; } else { m_of << "._" << v; } } } void emit_enum(const Span& sp, const ::HIR::GenericPath& p, const ::HIR::Enum& item) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "enum " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); ::HIR::TypeRef tmp; auto monomorph = [&](const auto& x)->const auto& { if( monomorphise_type_needed(x) ) { tmp = monomorphise_type(sp, item.m_params, p.m_params, x); m_resolve.expand_associated_types(sp, tmp); return tmp; } else { return x; } }; // TODO: Cache repr info elsewhere (and have core codegen be responsible instead) // TODO: Do more complex nonzero relations. ::std::vector nonzero_path; { // Detect Option (and similar enums) if( item.m_variants.size() == 2 && item.m_variants[0].second.is_Unit() && item.m_variants[1].second.is_Tuple() ) { const auto& data_ents = item.m_variants[1].second.as_Tuple(); if( data_ents.size() == 1 ) { const auto& data_type = monomorph(data_ents[0].ent); if( get_nonzero_path(sp, data_type, nonzero_path) ) { nonzero_path.push_back(0); ::std::reverse( nonzero_path.begin(), nonzero_path.end() ); } else { assert(nonzero_path.size() == 0); } } } } if( nonzero_path.size() > 0 ) { MIR_ASSERT(*m_mir_res, nonzero_path[0] == 0, ""); MIR_ASSERT(*m_mir_res, item.m_variants.size() == 2, ""); MIR_ASSERT(*m_mir_res, item.m_variants[0].second.is_Unit(), ""); const auto& data_var = item.m_variants[1]; MIR_ASSERT(*m_mir_res, data_var.second.is_Tuple(), ""); MIR_ASSERT(*m_mir_res, data_var.second.as_Tuple().size() == 1, ""); const auto& data_type = monomorph(data_var.second.as_Tuple()[0].ent); m_of << "struct e_" << Trans_Mangle(p) << " {\n"; m_of << "\t"; emit_ctype(data_type, FMT_CB(s, s << "_0";)); m_of << ";\n"; m_of << "};\n"; m_of << "struct e_" << Trans_Mangle(p) << " " << Trans_Mangle(::HIR::GenericPath(p.m_path + data_var.first, p.m_params.clone())) << "("; emit_ctype( data_type, FMT_CB(ss, ss << "_0";) ); m_of << ") {\n"; m_of << "\tstruct e_" << Trans_Mangle(p) << " rv = { ._0 = _0 };\n"; m_of << "\treturn rv;\n"; m_of << "}\n"; } else if( item.m_repr != ::HIR::Enum::Repr::Rust || ::std::all_of(item.m_variants.begin(), item.m_variants.end(), [](const auto& x){return x.second.is_Unit() || x.second.is_Value();}) ) { m_of << "struct e_" << Trans_Mangle(p) << " {\n"; switch(item.m_repr) { case ::HIR::Enum::Repr::Rust: case ::HIR::Enum::Repr::C: m_of << "\tunsigned int TAG;\n"; break; case ::HIR::Enum::Repr::U8: m_of << "\tuint8_t TAG;\n"; break; case ::HIR::Enum::Repr::U16: m_of << "\tuint16_t TAG;\n"; break; case ::HIR::Enum::Repr::U32: m_of << "\tuint32_t TAG;\n"; break; } m_of << "};\n"; } else { m_of << "struct e_" << Trans_Mangle(p) << " {\n"; m_of << "\tunsigned int TAG;\n"; m_of << "\tunion {\n"; for(unsigned int i = 0; i < item.m_variants.size(); i ++) { TU_MATCHA( (item.m_variants[i].second), (e), (Unit, m_of << "\t\tstruct {} var_" << i << ";\n"; ), (Value, m_of << "\t\tstruct {} var_" << i << ";\n"; ), (Tuple, m_of << "\t\tstruct {\n"; for(unsigned int i = 0; i < e.size(); i ++) { const auto& fld = e[i]; m_of << "\t\t\t"; emit_ctype( monomorph(fld.ent) ); m_of << " _" << i << ";\n"; } m_of << "\t\t} var_" << i << ";\n"; ), (Struct, m_of << "\t\tstruct {\n"; for(unsigned int i = 0; i < e.size(); i ++) { const auto& fld = e[i]; m_of << "\t\t\t"; emit_ctype( monomorph(fld.second.ent) ); m_of << " _" << i << ";\n"; } m_of << "\t\t} var_" << i << ";\n"; ) ) } m_of << "\t} DATA;\n"; m_of << "};\n"; // Constructors for tuple variants for(unsigned int var_idx = 0; var_idx < item.m_variants.size(); var_idx ++) { const auto& var = item.m_variants[var_idx]; TU_IFLET(::HIR::Enum::Variant, var.second, Tuple, e, m_of << "struct e_" << Trans_Mangle(p) << " " << Trans_Mangle(::HIR::GenericPath(p.m_path + var.first, p.m_params.clone())) << "("; for(unsigned int i = 0; i < e.size(); i ++) { if(i != 0) m_of << ", "; emit_ctype( monomorph(e[i].ent), FMT_CB(ss, ss << "_" << i;) ); } m_of << ") {\n"; m_of << "\tstruct e_" << Trans_Mangle(p) << " rv = { .TAG = " << var_idx << ", .DATA = {.var_" << var_idx << " = {"; for(unsigned int i = 0; i < e.size(); i ++) { if(i != 0) m_of << ","; m_of << "\n\t\t_" << i; } m_of << "\n\t\t}}};\n"; m_of << "\treturn rv;\n"; m_of << "}\n"; ) } } // --- // - Drop Glue // --- auto struct_ty = ::HIR::TypeRef(p.clone(), &item); auto drop_glue_path = ::HIR::Path(struct_ty.clone(), "#drop_glue"); auto struct_ty_ptr = ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Owned, struct_ty.clone()); auto drop_impl_path = (item.m_markings.has_drop_impl ? ::HIR::Path(struct_ty.clone(), m_resolve.m_lang_Drop, "drop") : ::HIR::Path(::HIR::SimplePath())); ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), struct_ty_ptr, {}, *(::MIR::Function*)nullptr }; m_mir_res = &mir_res; if( item.m_markings.has_drop_impl ) { m_of << "tUNIT " << Trans_Mangle(drop_impl_path) << "(struct e_" << Trans_Mangle(p) << "*rv);\n"; } m_of << "void " << Trans_Mangle(drop_glue_path) << "(struct e_" << Trans_Mangle(p) << "* rv) {\n"; // If this type has an impl of Drop, call that impl if( item.m_markings.has_drop_impl ) { m_of << "\t" << Trans_Mangle(drop_impl_path) << "(rv);\n"; } auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); if( nonzero_path.size() > 0 ) { auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); // TODO: Fat pointers? m_of << "\tif( ! (*rv)"; emit_nonzero_path(nonzero_path); m_of << " ) {\n"; for(const auto& fld : item.m_variants[1].second.as_Tuple()) { emit_destructor_call(fld_lv, monomorph(fld.ent), false); fld_lv.as_Field().field_index ++; } m_of << "\t}\n"; } else if( item.m_repr != ::HIR::Enum::Repr::Rust || ::std::all_of(item.m_variants.begin(), item.m_variants.end(), [](const auto& x){return x.second.is_Unit() || x.second.is_Value();}) ) { // Glue does nothing (except call the destructor, if there is one) } else { auto fld_lv = ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Downcast({ box$(self), 0 })), 0 }); m_of << "\tswitch(rv->TAG) {\n"; for(unsigned int var_idx = 0; var_idx < item.m_variants.size(); var_idx ++) { fld_lv.as_Field().val->as_Downcast().variant_index = var_idx; TU_MATCHA( (item.m_variants[var_idx].second), (e), (Unit, m_of << "\tcase " << var_idx << ": break;\n"; ), (Value, m_of << "\tcase " << var_idx << ": break;\n"; ), (Tuple, m_of << "\tcase " << var_idx << ":\n"; for(unsigned int i = 0; i < e.size(); i ++) { fld_lv.as_Field().field_index = i; const auto& fld = e[i]; emit_destructor_call(fld_lv, monomorph(fld.ent), false); } m_of << "\tbreak;\n"; ), (Struct, m_of << "\tcase " << var_idx << ":\n"; for(unsigned int i = 0; i < e.size(); i ++) { fld_lv.as_Field().field_index = i; const auto& fld = e[i]; emit_destructor_call(fld_lv, monomorph(fld.second.ent), false); } m_of << "\tbreak;\n"; ) ) } m_of << "\t}\n"; } m_of << "}\n"; m_mir_res = nullptr; if( nonzero_path.size() ) { m_enum_repr_cache.insert( ::std::make_pair( p.clone(), mv$(nonzero_path) ) ); } } void emit_static_ext(const ::HIR::Path& p, const ::HIR::Static& item, const Trans_Params& params) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "extern static " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); auto type = params.monomorph(m_resolve, item.m_type); m_of << "extern "; emit_ctype( type, FMT_CB(ss, ss << Trans_Mangle(p);) ); if( item.m_linkage.name != "" ) { m_of << " asm(\"" << item.m_linkage.name << "\")"; } m_of << ";"; m_of << "\t// static " << p << " : " << type; m_of << "\n"; m_mir_res = nullptr; } void emit_static_proto(const ::HIR::Path& p, const ::HIR::Static& item, const Trans_Params& params) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "static " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); auto type = params.monomorph(m_resolve, item.m_type); emit_ctype( type, FMT_CB(ss, ss << Trans_Mangle(p);) ); m_of << ";"; m_of << "\t// static " << p << " : " << type; m_of << "\n"; m_mir_res = nullptr; } void emit_static_local(const ::HIR::Path& p, const ::HIR::Static& item, const Trans_Params& params) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "static " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); auto type = params.monomorph(m_resolve, item.m_type); emit_ctype( type, FMT_CB(ss, ss << Trans_Mangle(p);) ); m_of << " = "; emit_literal(type, item.m_value_res, params); m_of << ";"; m_of << "\t// static " << p << " : " << type; m_of << "\n"; m_mir_res = nullptr; } void emit_literal(const ::HIR::TypeRef& ty, const ::HIR::Literal& lit, const Trans_Params& params) { TRACE_FUNCTION_F("ty=" << ty << ", lit=" << lit); ::HIR::TypeRef tmp; auto monomorph_with = [&](const ::HIR::PathParams& pp, const ::HIR::TypeRef& ty)->const ::HIR::TypeRef& { if( monomorphise_type_needed(ty) ) { tmp = monomorphise_type_with(sp, ty, monomorphise_type_get_cb(sp, nullptr, &pp, nullptr), false); m_resolve.expand_associated_types(sp, tmp); return tmp; } else { return ty; } }; auto get_inner_type = [&](unsigned int var, unsigned int idx)->const ::HIR::TypeRef& { TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Array, te, return *te.inner; ) else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Path, te, const auto& pp = te.path.m_data.as_Generic().m_params; TU_MATCHA((te.binding), (pbe), (Unbound, MIR_BUG(*m_mir_res, "Unbound type path " << ty); ), (Opaque, MIR_BUG(*m_mir_res, "Opaque type path " << ty); ), (Struct, TU_MATCHA( (pbe->m_data), (se), (Unit, MIR_BUG(*m_mir_res, "Unit struct " << ty); ), (Tuple, return monomorph_with(pp, se.at(idx).ent); ), (Named, return monomorph_with(pp, se.at(idx).second.ent); ) ) ), (Union, MIR_TODO(*m_mir_res, "Union literals"); ), (Enum, const auto& evar = pbe->m_variants.at(var); TU_MATCHA( (evar.second), (se), (Unit, MIR_BUG(*m_mir_res, "Unit enum var " << ty << " #" << var << " - fld " << idx); ), (Value, MIR_BUG(*m_mir_res, "Value enum var " << ty << " #" << var << " - fld " << idx); ), (Tuple, return monomorph_with(pp, se.at(idx).ent); ), (Struct, return monomorph_with(pp, se.at(idx).second.ent); ) ) ) ) throw ""; ) else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Tuple, te, return te.at(idx); ) else { MIR_TODO(*m_mir_res, "Unknown type in list literal - " << ty); } }; TU_MATCHA( (lit), (e), (Invalid, m_of << "/* INVALID */"; ), (List, if( ty.m_data.is_Array() ) m_of << "{"; m_of << "{"; for(unsigned int i = 0; i < e.size(); i ++) { if(i != 0) m_of << ","; m_of << " "; emit_literal(get_inner_type(0, i), e[i], params); } m_of << " }"; if( ty.m_data.is_Array() ) m_of << "}"; ), (Variant, MIR_ASSERT(*m_mir_res, ty.m_data.is_Path(), ""); MIR_ASSERT(*m_mir_res, ty.m_data.as_Path().binding.is_Enum(), ""); const auto& enm = *ty.m_data.as_Path().binding.as_Enum(); auto it = m_enum_repr_cache.find(ty.m_data.as_Path().path.m_data.as_Generic()); if( it != m_enum_repr_cache.end() ) { if( e.idx == 0 ) { m_of << "{0}"; } else { m_of << "{"; emit_literal(get_inner_type(e.idx, 0), e.vals[0], params); m_of << "}"; } } else if( enm.m_repr != ::HIR::Enum::Repr::Rust || ::std::all_of(enm.m_variants.begin(), enm.m_variants.end(), [](const auto& x){return x.second.is_Unit() || x.second.is_Value();}) ) { MIR_ASSERT(*m_mir_res, e.vals.empty(), "Value-only enum with fields"); m_of << "{" << e.idx << "}"; } else { m_of << "{" << e.idx << ", { .var_" << e.idx << " = {"; for(unsigned int i = 0; i < e.vals.size(); i ++) { if(i != 0) m_of << ","; m_of << " "; emit_literal(get_inner_type(e.idx, i), e.vals[i], params); } m_of << " }}}"; } ), (Integer, if( ty.m_data.is_Primitive() ) { switch(ty.m_data.as_Primitive()) { case ::HIR::CoreType::Bool: m_of << (e ? "true" : "false"); break; case ::HIR::CoreType::U8: m_of << ::std::hex << "0x" << (e & 0xFF) << ::std::dec; break; case ::HIR::CoreType::U16: m_of << ::std::hex << "0x" << (e & 0xFFFF) << ::std::dec; break; case ::HIR::CoreType::U32: m_of << ::std::hex << "0x" << (e & 0xFFFFFFFF) << ::std::dec; break; case ::HIR::CoreType::U64: case ::HIR::CoreType::U128: case ::HIR::CoreType::Usize: m_of << ::std::hex << "0x" << e << ::std::dec; break; case ::HIR::CoreType::I8: m_of << static_cast( static_cast(e) ); break; case ::HIR::CoreType::I16: m_of << static_cast(e); break; case ::HIR::CoreType::I32: m_of << static_cast(e); break; case ::HIR::CoreType::I64: case ::HIR::CoreType::I128: case ::HIR::CoreType::Isize: m_of << static_cast(e); break; case ::HIR::CoreType::Char: assert(0 <= e && e <= 0x10FFFF); if( e < 256 ) { m_of << e; } else { m_of << ::std::hex << "0x" << e << ::std::dec; } break; default: MIR_TODO(*m_mir_res, "Handle intger literal of type " << ty); } } else if( ty.m_data.is_Pointer() ) { m_of << ::std::hex << "(void*)0x" << e << ::std::dec; } else { MIR_BUG(*m_mir_res, "Integer literal for invalid type - " << ty); } ), (Float, m_of << e; ), (BorrowOf, TU_MATCHA( (e.m_data), (pe), (Generic, const auto& vi = m_crate.get_valitem_by_path(sp, pe.m_path); if( vi.is_Function() ) { if( !ty.m_data.is_Function() ) // TODO: Ensure that the type is `*const ()` or similar. m_of << "(void*)"; else ; } else { if( ty.m_data.is_Borrow() && ty.m_data.as_Borrow().inner->m_data.is_Slice() ) { // Since this is a borrow, it must be of an array. MIR_ASSERT(*m_mir_res, vi.is_Static(), "BorrowOf returning &[T] not of a static - " << pe.m_path << " is " << vi.tag_str()); const auto& stat = vi.as_Static(); MIR_ASSERT(*m_mir_res, stat.m_type.m_data.is_Array(), "BorrowOf : &[T] of non-array static, " << pe.m_path << " - " << stat.m_type); unsigned int size = stat.m_type.m_data.as_Array().size_val; m_of << "{ &" << Trans_Mangle( params.monomorph(m_resolve, e)) << ", " << size << "}"; return ; } else m_of << "&"; } ), (UfcsUnknown, MIR_BUG(*m_mir_res, "UfcsUnknown in trans " << e); ), (UfcsInherent, m_of << "&"; ), (UfcsKnown, m_of << "&"; ) ) m_of << Trans_Mangle( params.monomorph(m_resolve, e)); ), (String, m_of << "{ "; m_of << "\"" << ::std::hex; for(const auto& v : e) { switch(v) { case '"': m_of << "\\\""; break; case '\\': m_of << "\\\\"; break; case '\n': m_of << "\\n"; break; default: if( ' ' <= v ) m_of << v; else if( v < 16 ) m_of << "\\x0" << (unsigned int)static_cast(v) << "\"\""; else m_of << "\\x" << (unsigned int)static_cast(v) << "\"\""; } } m_of << "\"" << ::std::dec; m_of << ", " << e.size() << "}"; ) ) } void emit_vtable(const ::HIR::Path& p, const ::HIR::Trait& trait) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "vtable " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); const auto& trait_path = p.m_data.as_UfcsKnown().trait; const auto& type = *p.m_data.as_UfcsKnown().type; // TODO: Hack in fn pointer VTable handling if( const auto* te = type.m_data.opt_Function() ) { const char* call_fcn_name = nullptr; if( trait_path.m_path == m_resolve.m_lang_Fn ) call_fcn_name = "call"; else if( trait_path.m_path == m_resolve.m_lang_FnMut ) call_fcn_name = "call_mut"; else if( trait_path.m_path == m_resolve.m_lang_FnOnce ) call_fcn_name = "call_once"; else ; if( call_fcn_name ) { auto fcn_p = p.clone(); fcn_p.m_data.as_UfcsKnown().item = call_fcn_name; emit_ctype(*te->m_rettype); auto arg_ty = ::HIR::TypeRef::new_unit(); for(const auto& ty : te->m_arg_types) arg_ty.m_data.as_Tuple().push_back( ty.clone() ); m_of << " " << Trans_Mangle(fcn_p) << "("; emit_ctype(type, FMT_CB(ss, ss << "*ptr";)); m_of << ", "; emit_ctype(arg_ty, FMT_CB(ss, ss << "args";)); m_of << ") {\n"; m_of << "\treturn (*ptr)("; for(unsigned int i = 0; i < te->m_arg_types.size(); i++) { if(i != 0) m_of << ", "; m_of << "args._" << i; } m_of << ");\n"; m_of << "}\n"; } } { auto vtable_sp = trait_path.m_path; vtable_sp.m_components.back() += "#vtable"; auto vtable_params = trait_path.m_params.clone(); for(const auto& ty : trait.m_type_indexes) { auto aty = ::HIR::TypeRef( ::HIR::Path( type.clone(), trait_path.clone(), ty.first ) ); m_resolve.expand_associated_types(sp, aty); vtable_params.m_types.push_back( mv$(aty) ); } const auto& vtable_ref = m_crate.get_struct_by_path(sp, vtable_sp); ::HIR::TypeRef vtable_ty( ::HIR::GenericPath(mv$(vtable_sp), mv$(vtable_params)), &vtable_ref ); emit_ctype(vtable_ty); m_of << " " << Trans_Mangle(p) << " = {\n"; } auto monomorph_cb_trait = monomorphise_type_get_cb(sp, &type, &trait_path.m_params, nullptr); // Size, Alignment, and destructor m_of << "\t{ "; m_of << "sizeof("; emit_ctype(type); m_of << "),"; m_of << "__alignof__("; emit_ctype(type); m_of << "),"; // TODO: Drop glue pointer m_of << "}"; // No newline, added below for(unsigned int i = 0; i < trait.m_value_indexes.size(); i ++ ) { m_of << ",\n"; for(const auto& m : trait.m_value_indexes) { if( m.second.first != i ) continue ; //MIR_ASSERT(*m_mir_res, tr.m_values.at(m.first).is_Function(), "TODO: Handle generating vtables with non-function items"); DEBUG("- " << m.second.first << " = " << m.second.second << " :: " << m.first); auto gpath = monomorphise_genericpath_with(sp, m.second.second, monomorph_cb_trait, false); // NOTE: `void*` cast avoids mismatched pointer type errors due to the receiver being &mut()/&() in the vtable m_of << "\t(void*)" << Trans_Mangle( ::HIR::Path(type.clone(), mv$(gpath), m.first) ); } } m_of << "\n"; m_of << "\t};\n"; m_mir_res = nullptr; } void emit_function_ext(const ::HIR::Path& p, const ::HIR::Function& item, const Trans_Params& params) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "extern fn " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); m_of << "// extern \"" << item.m_abi << "\" " << p << "\n"; m_of << "extern "; emit_function_header(p, item, params); if( item.m_linkage.name != "" ) { m_of << " asm(\"" << item.m_linkage.name << "\")"; } m_of << ";\n"; m_mir_res = nullptr; } void emit_function_proto(const ::HIR::Path& p, const ::HIR::Function& item, const Trans_Params& params) override { ::MIR::TypeResolve top_mir_res { sp, m_resolve, FMT_CB(ss, ss << "/*proto*/ fn " << p;), ::HIR::TypeRef(), {}, *(::MIR::Function*)nullptr }; m_mir_res = &top_mir_res; TRACE_FUNCTION_F(p); if( item.m_linkage.name != "" ) { m_of << "#define " << Trans_Mangle(p) << " " << item.m_linkage.name << "\n"; } emit_function_header(p, item, params); m_of << ";\n"; m_mir_res = nullptr; } void emit_function_code(const ::HIR::Path& p, const ::HIR::Function& item, const Trans_Params& params, const ::MIR::FunctionPointer& code) override { TRACE_FUNCTION_F(p); ::MIR::TypeResolve::args_t arg_types; for(const auto& ent : item.m_args) arg_types.push_back(::std::make_pair( ::HIR::Pattern{}, params.monomorph(m_resolve, ent.second) )); ::HIR::TypeRef ret_type_tmp; const auto& ret_type = monomorphise_fcn_return(ret_type_tmp, item, params); ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << p;), ret_type, arg_types, *code }; m_mir_res = &mir_res; m_of << "// " << p << "\n"; emit_function_header(p, item, params); m_of << "\n"; m_of << "{\n"; // Variables m_of << "\t"; emit_ctype(ret_type, FMT_CB(ss, ss << "rv";)); m_of << ";\n"; for(unsigned int i = 0; i < code->named_variables.size(); i ++) { DEBUG("var" << i << " : " << code->named_variables[i]); m_of << "\t"; emit_ctype(code->named_variables[i], FMT_CB(ss, ss << "var" << i;)); m_of << ";"; m_of << "\t// " << code->named_variables[i]; m_of << "\n"; } for(unsigned int i = 0; i < code->temporaries.size(); i ++) { DEBUG("tmp" << i << " : " << code->temporaries[i]); m_of << "\t"; emit_ctype(code->temporaries[i], FMT_CB(ss, ss << " tmp" << i;)); m_of << ";"; m_of << "\t// " << code->temporaries[i]; m_of << "\n"; } for(unsigned int i = 0; i < code->drop_flags.size(); i ++) { m_of << "\tbool df" << i << " = " << code->drop_flags[i] << ";\n"; } for(unsigned int i = 0; i < code->blocks.size(); i ++) { TRACE_FUNCTION_F(p << " bb" << i); if( code->blocks[i].statements.size() == 0 && code->blocks[i].terminator.is_Diverge() ) { DEBUG("- Diverge only, omitting"); m_of << "bb" << i << ": _Unwind_Resume(); // Diverge\n"; continue ; } m_of << "bb" << i << ":\n"; for(const auto& stmt : code->blocks[i].statements) { mir_res.set_cur_stmt(i, (&stmt - &code->blocks[i].statements.front())); switch( stmt.tag() ) { case ::MIR::Statement::TAGDEAD: throw ""; case ::MIR::Statement::TAG_SetDropFlag: { const auto& e = stmt.as_SetDropFlag(); m_of << "\tdf" << e.idx << " = "; if( e.other == ~0u ) m_of << e.new_val; else m_of << (e.new_val ? "!" : "") << "df" << e.other; m_of << ";\n"; break; } case ::MIR::Statement::TAG_Drop: { const auto& e = stmt.as_Drop(); ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e.slot); if( e.flag_idx != ~0u ) m_of << "if( df" << e.flag_idx << " ) {\n"; switch( e.kind ) { case ::MIR::eDropKind::SHALLOW: // Shallow drops are only valid on owned_box if( const auto* ity = m_resolve.is_type_owned_box(ty) ) { // Emit a call to box_free for the type ::HIR::GenericPath box_free { m_crate.get_lang_item_path(sp, "box_free"), { ity->clone() } }; // TODO: This is specific to the official liballoc's owned_box m_of << "\t" << Trans_Mangle(box_free) << "("; emit_lvalue(e.slot); m_of << "._0._0._0);\n"; } else { MIR_BUG(mir_res, "Shallow drop on non-Box - " << ty); } break; case ::MIR::eDropKind::DEEP: emit_destructor_call(e.slot, ty, false); break; } if( e.flag_idx != ~0u ) m_of << "}\n"; break; } case ::MIR::Statement::TAG_Asm: { const auto& e = stmt.as_Asm(); struct H { static bool has_flag(const ::std::vector<::std::string>& flags, const char* des) { return ::std::find_if(flags.begin(), flags.end(), [des](const auto&x){return x==des;}) != flags.end(); } static const char* convert_reg(const char* r) { if( ::std::strcmp(r, "{eax}") == 0 || ::std::strcmp(r, "{rax}") == 0 ) { return "a"; } else { return r; } } }; bool is_volatile = H::has_flag(e.flags, "volatile"); bool is_intel = H::has_flag(e.flags, "intel"); m_of << "\t__asm__ "; if(is_volatile) m_of << "__volatile__"; // TODO: Convert format string? m_of << "(\"" << (is_intel ? ".syntax intel; " : "") << FmtEscaped(e.tpl) << (is_intel ? ".syntax att; " : "") << "\""; m_of << ": "; for(unsigned int i = 0; i < e.outputs.size(); i ++ ) { const auto& v = e.outputs[i]; if( i != 0 ) m_of << ", "; m_of << "\""; switch(v.first[0]) { case '=': m_of << "="; break; case '+': m_of << "+"; break; default: MIR_TODO(mir_res, "Handle asm! output leader '" << v.first[0] << "'"); } m_of << H::convert_reg(v.first.c_str()+1); m_of << "\"("; emit_lvalue(v.second); m_of << ")"; } m_of << ": "; for(unsigned int i = 0; i < e.inputs.size(); i ++ ) { const auto& v = e.inputs[i]; if( i != 0 ) m_of << ", "; m_of << "\"" << v.first << "\"("; emit_lvalue(v.second); m_of << ")"; } m_of << ": "; for(unsigned int i = 0; i < e.clobbers.size(); i ++ ) { if( i != 0 ) m_of << ", "; m_of << "\"" << e.clobbers[i] << "\""; } m_of << ");\n"; break; } case ::MIR::Statement::TAG_Assign: { const auto& e = stmt.as_Assign(); DEBUG("- " << e.dst << " = " << e.src); m_of << "\t"; TU_MATCHA( (e.src), (ve), (Use, ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, ve); if( ty == ::HIR::TypeRef::new_diverge() ) { m_of << "abort()"; break; } emit_lvalue(e.dst); m_of << " = "; emit_lvalue(ve); ), (Constant, TU_MATCHA( (ve), (c), (Int, emit_lvalue(e.dst); m_of << " = "; if( c == INT64_MIN ) m_of << "INT64_MIN"; else m_of << c; ), (Uint, ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e.dst); emit_lvalue(e.dst); m_of << " = "; switch(ty.m_data.as_Primitive()) { case ::HIR::CoreType::U8: m_of << ::std::hex << "0x" << (c & 0xFF) << ::std::dec; break; case ::HIR::CoreType::U16: m_of << ::std::hex << "0x" << (c & 0xFFFF) << ::std::dec; break; case ::HIR::CoreType::U32: m_of << ::std::hex << "0x" << (c & 0xFFFFFFFF) << ::std::dec; break; case ::HIR::CoreType::U64: case ::HIR::CoreType::U128: case ::HIR::CoreType::Usize: m_of << ::std::hex << "0x" << c << ::std::dec; break; case ::HIR::CoreType::Char: assert(0 <= c && c <= 0x10FFFF); if( c < 256 ) { m_of << c; } else { m_of << ::std::hex << "0x" << c << ::std::dec; } break; default: MIR_BUG(*m_mir_res, "Invalid type for UInt literal - " << ty); } ), (Float, emit_lvalue(e.dst); m_of << " = "; if( ::std::isnan(c) ) { m_of << "NAN"; } else if( ::std::isinf(c) ) { m_of << "INFINITY"; } else { m_of << c; } ), (Bool, emit_lvalue(e.dst); m_of << " = "; m_of << (c ? "true" : "false"); ), (Bytes, ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e.dst); MIR_ASSERT(mir_res, ty.m_data.is_Borrow(), "Const::Bytes returning non-borrow - " << ty); if( ty.m_data.as_Borrow().inner->m_data.is_Array() ) { // Array borrow : Cast the C string to the array emit_lvalue(e.dst); m_of << " = "; // - Laziness m_of << "(void*)\"" << ::std::oct; for(const auto& v : c) { if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) m_of << v; else m_of << "\\" << (unsigned int)v; } m_of << "\"" << ::std::dec; } else { // Slice borrow (asumed), pointer and metadata emit_lvalue(e.dst); m_of << ".PTR = "; m_of << "\"" << ::std::oct; for(const auto& v : c) { if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) m_of << v; else m_of << "\\" << (unsigned int)v; } m_of << "\"" << ::std::dec; m_of << ";\n\t"; emit_lvalue(e.dst); m_of << ".META = " << c.size(); } ), (StaticString, emit_lvalue(e.dst); m_of << ".PTR = "; m_of << "\"" << ::std::oct; for(const auto& v : c) { if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) m_of << v; else m_of << "\\" << (unsigned int)v; } m_of << "\"" << ::std::dec; m_of << ";\n\t"; emit_lvalue(e.dst); m_of << ".META = " << c.size(); ), (Const, // TODO: This should have been eliminated? ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e.dst); const auto& lit = get_literal_for_const(c.p); assign_from_literal([&](){ emit_lvalue(e.dst); }, ty, lit); ), (ItemAddr, emit_lvalue(e.dst); m_of << " = "; TU_MATCHA( (c.m_data), (pe), (Generic, if( pe.m_path.m_components.size() > 1 && m_crate.get_typeitem_by_path(sp, pe.m_path, false, true).is_Enum() ) ; else { const auto& vi = m_crate.get_valitem_by_path(sp, pe.m_path); if( vi.is_Function() || vi.is_StructConstructor() ) { } else { m_of << "&"; } } ), (UfcsUnknown, MIR_BUG(*m_mir_res, "UfcsUnknown in trans " << c); ), (UfcsInherent, // TODO: If the target is a function, don't emit the & m_of << "&"; ), (UfcsKnown, // TODO: If the target is a function, don't emit the & m_of << "&"; ) ) m_of << Trans_Mangle(c); ) ) ), (SizedArray, if( ve.count == 0 ) { } else if( ve.count == 1 ) { emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_lvalue(ve.val); } else if( ve.count == 2 ) { emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_lvalue(ve.val); m_of << ";\n\t"; emit_lvalue(e.dst); m_of << ".DATA[1] = "; emit_lvalue(ve.val); } else if( ve.count == 3 ) { emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_lvalue(ve.val); m_of << ";\n\t"; emit_lvalue(e.dst); m_of << ".DATA[1] = "; emit_lvalue(ve.val); m_of << ";\n\t"; emit_lvalue(e.dst); m_of << ".DATA[2] = "; emit_lvalue(ve.val); } else { m_of << "for(unsigned int i = 0; i < " << ve.count << "; i ++)\n"; m_of << "\t\t"; emit_lvalue(e.dst); m_of << ".DATA[i] = "; emit_lvalue(ve.val); } ), (Borrow, ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, ve.val); bool special = false; // If the inner value has type [T] or str, create DST based on inner pointer and existing metadata TU_IFLET(::MIR::LValue, ve.val, Deref, le, if( metadata_type(ty) != MetadataType::None ) { emit_lvalue(e.dst); m_of << " = "; emit_lvalue(*le.val); special = true; } ) // Magic for taking a &-ptr to unsized field of a struct. // - Needs to get metadata from bottom-level pointer. else TU_IFLET(::MIR::LValue, ve.val, Field, le, if( metadata_type(ty) != MetadataType::None ) { const ::MIR::LValue* base_val = &*le.val; while(base_val->is_Field()) base_val = &*base_val->as_Field().val; MIR_ASSERT(mir_res, base_val->is_Deref(), "DST access must be via a deref"); const ::MIR::LValue& base_ptr = *base_val->as_Deref().val; // Construct the new DST emit_lvalue(e.dst); m_of << ".META = "; emit_lvalue(base_ptr); m_of << ".META;\n\t"; emit_lvalue(e.dst); m_of << ".PTR = &"; emit_lvalue(ve.val); special = true; } ) if( !special ) { emit_lvalue(e.dst); m_of << " = "; m_of << "& "; emit_lvalue(ve.val); } ), (Cast, if( m_resolve.is_type_phantom_data(ve.type) ) { m_of << "/* PhandomData cast */\n"; continue ; } emit_lvalue(e.dst); m_of << " = "; m_of << "("; emit_ctype(ve.type); m_of << ")"; // TODO: If the source is an unsized borrow, then extract the pointer bool special = false; ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, ve.val); // If the destination is a thin pointer if( ve.type.m_data.is_Pointer() && !is_dst( *ve.type.m_data.as_Pointer().inner ) ) { // NOTE: Checks the result of the deref if( (ty.m_data.is_Borrow() && is_dst(*ty.m_data.as_Borrow().inner)) || (ty.m_data.is_Pointer() && is_dst(*ty.m_data.as_Pointer().inner)) ) { emit_lvalue(ve.val); m_of << ".PTR"; special = true; } } if( ve.type.m_data.is_Primitive() && ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum() ) { emit_lvalue(ve.val); m_of << ".TAG"; special = true; } if( !special ) { emit_lvalue(ve.val); } ), (BinOp, emit_lvalue(e.dst); m_of << " = "; ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, ve.val_l); if( ty.m_data.is_Borrow() ) { m_of << "(slice_cmp("; emit_lvalue(ve.val_l); m_of << ", "; emit_lvalue(ve.val_r); m_of << ")"; switch(ve.op) { case ::MIR::eBinOp::EQ: m_of << " == 0"; break; case ::MIR::eBinOp::NE: m_of << " != 0"; break; case ::MIR::eBinOp::GT: m_of << " > 0"; break; case ::MIR::eBinOp::GE: m_of << " >= 0"; break; case ::MIR::eBinOp::LT: m_of << " < 0"; break; case ::MIR::eBinOp::LE: m_of << " <= 0"; break; default: MIR_BUG(mir_res, "Unknown comparison of a &-ptr - " << e.src << " with " << ty); } m_of << ")"; break; } else if( const auto* te = ty.m_data.opt_Pointer() ) { if( metadata_type(*te->inner) != MetadataType::None ) { switch(ve.op) { case ::MIR::eBinOp::EQ: emit_lvalue(ve.val_l); m_of << ".PTR == "; emit_lvalue(ve.val_r); m_of << ".PTR && "; emit_lvalue(ve.val_l); m_of << ".META == "; emit_lvalue(ve.val_r); m_of << ".META"; break; case ::MIR::eBinOp::NE: emit_lvalue(ve.val_l); m_of << ".PTR != "; emit_lvalue(ve.val_r); m_of << ".PTR || "; emit_lvalue(ve.val_l); m_of << ".META != "; emit_lvalue(ve.val_r); m_of << ".META"; break; default: MIR_BUG(mir_res, "Unknown comparison of a *-ptr - " << e.src << " with " << ty); } } else { emit_lvalue(ve.val_l); switch(ve.op) { case ::MIR::eBinOp::EQ: m_of << " == "; break; case ::MIR::eBinOp::NE: m_of << " != "; break; case ::MIR::eBinOp::GT: m_of << " > " ; break; case ::MIR::eBinOp::GE: m_of << " >= "; break; case ::MIR::eBinOp::LT: m_of << " < " ; break; case ::MIR::eBinOp::LE: m_of << " <= "; break; default: MIR_BUG(mir_res, "Unknown comparison of a *-ptr - " << e.src << " with " << ty); } emit_lvalue(ve.val_r); } break; } else if( ve.op == ::MIR::eBinOp::MOD && (ty == ::HIR::CoreType::F32 || ty == ::HIR::CoreType::F64) ) { if( ty == ::HIR::CoreType::F32 ) m_of << "remainderf"; else m_of << "remainder"; m_of << "("; emit_lvalue(ve.val_l); m_of << ", "; emit_lvalue(ve.val_r); m_of << ")"; break; } else { } emit_lvalue(ve.val_l); switch(ve.op) { case ::MIR::eBinOp::ADD: m_of << " + "; break; case ::MIR::eBinOp::SUB: m_of << " - "; break; case ::MIR::eBinOp::MUL: m_of << " * "; break; case ::MIR::eBinOp::DIV: m_of << " / "; break; case ::MIR::eBinOp::MOD: m_of << " % "; break; case ::MIR::eBinOp::BIT_OR: m_of << " | "; break; case ::MIR::eBinOp::BIT_AND: m_of << " & "; break; case ::MIR::eBinOp::BIT_XOR: m_of << " ^ "; break; case ::MIR::eBinOp::BIT_SHR: m_of << " >> "; break; case ::MIR::eBinOp::BIT_SHL: m_of << " << "; break; case ::MIR::eBinOp::EQ: m_of << " == "; break; case ::MIR::eBinOp::NE: m_of << " != "; break; case ::MIR::eBinOp::GT: m_of << " > " ; break; case ::MIR::eBinOp::GE: m_of << " >= "; break; case ::MIR::eBinOp::LT: m_of << " < " ; break; case ::MIR::eBinOp::LE: m_of << " <= "; break; case ::MIR::eBinOp::ADD_OV: case ::MIR::eBinOp::SUB_OV: case ::MIR::eBinOp::MUL_OV: case ::MIR::eBinOp::DIV_OV: MIR_TODO(mir_res, "Overflow"); break; } emit_lvalue(ve.val_r); ), (UniOp, ::HIR::TypeRef tmp; emit_lvalue(e.dst); m_of << " = "; switch(ve.op) { case ::MIR::eUniOp::NEG: m_of << "-"; break; case ::MIR::eUniOp::INV: if( mir_res.get_lvalue_type(tmp, e.dst) == ::HIR::CoreType::Bool ) m_of << "!"; else m_of << "~"; break; } emit_lvalue(ve.val); ), (DstMeta, emit_lvalue(e.dst); m_of << " = "; emit_lvalue(ve.val); m_of << ".META"; ), (DstPtr, emit_lvalue(e.dst); m_of << " = "; emit_lvalue(ve.val); m_of << ".PTR"; ), (MakeDst, emit_lvalue(e.dst); m_of << ".PTR = "; emit_lvalue(ve.ptr_val); m_of << ";\n\t"; emit_lvalue(e.dst); m_of << ".META = "; emit_lvalue(ve.meta_val); ), (Tuple, for(unsigned int j = 0; j < ve.vals.size(); j ++) { if( j != 0 ) m_of << ";\n\t"; emit_lvalue(e.dst); m_of << "._" << j << " = "; emit_lvalue(ve.vals[j]); } ), (Array, for(unsigned int j = 0; j < ve.vals.size(); j ++) { if( j != 0 ) m_of << ";\n\t"; emit_lvalue(e.dst); m_of << ".DATA[" << j << "] = "; emit_lvalue(ve.vals[j]); } ), (Variant, if( m_crate.get_typeitem_by_path(sp, ve.path.m_path).is_Union() ) { emit_lvalue(e.dst); } else { MIR_TODO(mir_res, "Construct enum with RValue::Variant"); emit_lvalue(e.dst); m_of << ".TAG = " << ve.index << ";\n\t"; emit_lvalue(e.dst); m_of << ".DATA"; } m_of << ".var_" << ve.index << " = "; emit_lvalue(ve.val); ), (Struct, if(ve.variant_idx != ~0u) { ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e.dst); if( ty.m_data.as_Path().binding.is_Enum() ) { auto it = m_enum_repr_cache.find(ty.m_data.as_Path().path.m_data.as_Generic()); if( it != m_enum_repr_cache.end() ) { if( ve.variant_idx == 0 ) { // TODO: Use nonzero_path m_of << "memset(&"; emit_lvalue(e.dst); m_of << ", 0, sizeof("; emit_ctype(ty); m_of << "))"; } else if( ve.variant_idx == 1 ) { emit_lvalue(e.dst); m_of << "._0 = "; emit_lvalue(ve.vals[0]); } else { } break; } } emit_lvalue(e.dst); m_of << ".TAG = " << ve.variant_idx; if(ve.vals.size() > 0) m_of << ";\n\t"; } for(unsigned int j = 0; j < ve.vals.size(); j ++) { // HACK: Don't emit assignment of PhantomData ::HIR::TypeRef tmp; if( m_resolve.is_type_phantom_data( mir_res.get_lvalue_type(tmp, ve.vals[j])) ) continue ; if( j != 0 ) m_of << ";\n\t"; emit_lvalue(e.dst); if(ve.variant_idx != ~0u) m_of << ".DATA.var_" << ve.variant_idx; m_of << "._" << j << " = "; emit_lvalue(ve.vals[j]); } ) ) m_of << ";"; m_of << "\t// " << e.dst << " = " << e.src; m_of << "\n"; break; } } } mir_res.set_cur_stmt_term(i); DEBUG("- " << code->blocks[i].terminator); TU_MATCHA( (code->blocks[i].terminator), (e), (Incomplete, m_of << "\tfor(;;);\n"; ), (Return, m_of << "\treturn rv;\n"; ), (Diverge, m_of << "\t_Unwind_Resume();\n"; ), (Goto, m_of << "\tgoto bb" << e << ";\n"; ), (Panic, m_of << "\tgoto bb" << e << "; /* panic */\n"; ), (If, m_of << "\tif("; emit_lvalue(e.cond); m_of << ") goto bb" << e.bb0 << "; else goto bb" << e.bb1 << ";\n"; ), (Switch, ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e.val); MIR_ASSERT(mir_res, ty.m_data.is_Path(), ""); MIR_ASSERT(mir_res, ty.m_data.as_Path().binding.is_Enum(), ""); auto it = m_enum_repr_cache.find( ty.m_data.as_Path().path.m_data.as_Generic() ); if( it != m_enum_repr_cache.end() ) { MIR_ASSERT(mir_res, e.targets.size() == 2, ""); m_of << "\tif("; emit_lvalue(e.val); emit_nonzero_path(it->second); m_of << ")\n"; m_of << "\t\tgoto bb" << e.targets[1] << ";\n"; m_of << "\telse\n"; m_of << "\t\tgoto bb" << e.targets[0] << ";\n"; } else { m_of << "\tswitch("; emit_lvalue(e.val); m_of << ".TAG) {\n"; for(unsigned int j = 0; j < e.targets.size(); j ++) m_of << "\t\tcase " << j << ": goto bb" << e.targets[j] << ";\n"; m_of << "\t\tdefault: abort();\n"; m_of << "\t}\n"; } ), (Call, m_of << "\t"; if( e.fcn.is_Intrinsic() ) { const auto& name = e.fcn.as_Intrinsic().name; const auto& params = e.fcn.as_Intrinsic().params; emit_intrinsic_call(name, params, e); m_of << "\tgoto bb" << e.ret_block << ";\n"; break ; } TU_MATCHA( (e.fcn), (e2), (Value, { ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e2); MIR_ASSERT(mir_res, ty.m_data.is_Function(), "Call::Value on non-function - " << ty); if( !ty.m_data.as_Function().m_rettype->m_data.is_Diverge() ) { emit_lvalue(e.ret_val); m_of << " = "; } } m_of << "("; emit_lvalue(e2); m_of << ")"; ), (Path, { bool is_diverge = false; TU_MATCHA( (e2.m_data), (pe), (Generic, const auto& fcn = m_crate.get_function_by_path(sp, pe.m_path); is_diverge |= fcn.m_return.m_data.is_Diverge(); // TODO: Monomorph. ), (UfcsUnknown, ), (UfcsInherent, // TODO: Check if the return type is ! is_diverge |= m_resolve.m_crate.find_type_impls(*pe.type, [&](const auto& ty)->const auto& { return ty; }, [&](const auto& impl) { // Associated functions { auto it = impl.m_methods.find(pe.item); if( it != impl.m_methods.end() ) { return it->second.data.m_return.m_data.is_Diverge(); } } // Associated static (undef) return false; }); ), (UfcsKnown, // TODO: Check if the return type is ! ) ) if(!is_diverge) { emit_lvalue(e.ret_val); m_of << " = "; } } m_of << Trans_Mangle(e2); ), (Intrinsic, MIR_BUG(mir_res, "Intrinsic not expected, should be handled above"); ) ) m_of << "("; for(unsigned int j = 0; j < e.args.size(); j ++) { if(j != 0) m_of << ","; m_of << " "; emit_lvalue(e.args[j]); } m_of << " );\n"; m_of << "\tgoto bb" << e.ret_block << ";\n"; ) ) m_of << "\t// ^ " << code->blocks[i].terminator << "\n"; } m_of << "}\n"; m_of.flush(); m_mir_res = nullptr; } private: const ::HIR::TypeRef& monomorphise_fcn_return(::HIR::TypeRef& tmp, const ::HIR::Function& item, const Trans_Params& params) { if( visit_ty_with(item.m_return, [&](const auto& x){ return x.m_data.is_ErasedType() || x.m_data.is_Generic(); }) ) { tmp = clone_ty_with(Span(), item.m_return, [&](const auto& tpl, auto& out){ TU_IFLET( ::HIR::TypeRef::Data, tpl.m_data, ErasedType, e, out = params.monomorph(m_resolve, item.m_code.m_erased_types.at(e.m_index)); return true; ) else if( tpl.m_data.is_Generic() ) { out = params.get_cb()(tpl).clone(); return true; } else { return false; } }); m_resolve.expand_associated_types(Span(), tmp); return tmp; } else { return item.m_return; } } void emit_function_header(const ::HIR::Path& p, const ::HIR::Function& item, const Trans_Params& params) { ::HIR::TypeRef tmp; const auto& ret_ty = monomorphise_fcn_return(tmp, item, params); emit_ctype( ret_ty, FMT_CB(ss, ss << " " << Trans_Mangle(p) << "("; if( item.m_args.size() == 0 ) { ss << "void)"; } else { for(unsigned int i = 0; i < item.m_args.size(); i ++) { if( i != 0 ) m_of << ","; ss << "\n\t\t"; this->emit_ctype( params.monomorph(m_resolve, item.m_args[i].second), FMT_CB(os, os << "arg" << i;) ); } if( item.m_variadic ) m_of << ", ..."; ss << "\n\t\t)"; } )); } void emit_intrinsic_call(const ::std::string& name, const ::HIR::PathParams& params, const ::MIR::Terminator::Data_Call& e) { const auto& mir_res = *m_mir_res; struct H { static const char* get_atomic_ordering(const ::MIR::TypeResolve& mir_res, const ::std::string& name, size_t prefix_len) { if( name.size() < prefix_len ) return "memory_order_seq_cst"; const char* suffix = name.c_str() + prefix_len; if( ::std::strcmp(suffix, "acq") == 0 ) { return "memory_order_acquire"; } else if( ::std::strcmp(suffix, "rel") == 0 ) { return "memory_order_release"; } else if( ::std::strcmp(suffix, "relaxed") == 0 ) { return "memory_order_relaxed"; } else if( ::std::strcmp(suffix, "acqrel") == 0 ) { return "memory_order_acq_rel"; } else { MIR_BUG(mir_res, "Unknown atomic ordering suffix - '" << suffix << "'"); } } }; auto emit_atomic_cxchg = [&](const auto& e, const char* o_succ, const char* o_fail, bool is_weak) { emit_lvalue(e.ret_val); m_of << "._0 = "; emit_lvalue(e.args.at(1)); m_of << ";\n\t"; emit_lvalue(e.ret_val); m_of << "._1 = atomic_compare_exchange_" << (is_weak ? "weak" : "strong") << "_explicit("; m_of << "(_Atomic "; emit_ctype(params.m_types.at(0)); m_of << "*)"; emit_lvalue(e.args.at(0)); m_of << ", &"; emit_lvalue(e.ret_val); m_of << "._0"; m_of << ", "; emit_lvalue(e.args.at(2)); m_of << ", "<inner); m_of << ")"; } else if( inner_ty == ::HIR::CoreType::Str ) { if( ! ty.m_data.is_Primitive() ) { m_of << "sizeof("; emit_ctype(ty); m_of << ") + "; } emit_lvalue(e.args.at(0)); m_of << ".META"; } else if( inner_ty.m_data.is_TraitObject() ) { if( ! ty.m_data.is_TraitObject() ) { m_of << "sizeof("; emit_ctype(ty); m_of << ") + "; } m_of << "((VTABLE_HDR*)"; emit_lvalue(e.args.at(0)); m_of << ".META)->size"; } else { MIR_BUG(mir_res, "Unknown inner unsized type " << inner_ty << " for " << ty); } #else switch( metadata_type(ty) ) { case MetadataType::None: m_of << "sizeof("; emit_ctype(ty); m_of << ")"; break; case MetadataType::Slice: { // TODO: Have a function that fetches the inner type for types like `Path` or `str` const auto& ity = *ty.m_data.as_Slice().inner; emit_lvalue(e.args.at(0)); m_of << ".META * sizeof("; emit_ctype(ity); m_of << ")"; break; } case MetadataType::TraitObject: m_of << "((VTABLE_HDR*)"; emit_lvalue(e.args.at(0)); m_of << ".META)->size"; break; } #endif } else if( name == "min_align_of_val" ) { emit_lvalue(e.ret_val); m_of << " = "; const auto& ty = params.m_types.at(0); #if 1 auto inner_ty = get_inner_unsized_type(ty); if( inner_ty == ::HIR::TypeRef() ) { m_of << "__alignof__("; emit_ctype(ty); m_of << ")"; } else if( const auto* te = inner_ty.m_data.opt_Slice() ) { if( ! ty.m_data.is_Slice() ) { m_of << "max( __alignof__("; emit_ctype(ty); m_of << "), "; } m_of << "__alignof__("; emit_ctype(*te->inner); m_of << ")"; if( ! ty.m_data.is_Slice() ) { m_of << " )"; } } else if( inner_ty == ::HIR::CoreType::Str ) { if( ! ty.m_data.is_Primitive() ) { m_of << "__alignof__("; emit_ctype(ty); m_of << ")"; } else { m_of << "1"; } } else if( inner_ty.m_data.is_TraitObject() ) { if( ! ty.m_data.is_TraitObject() ) { m_of << "max( __alignof__("; emit_ctype(ty); m_of << "), "; } m_of << "((VTABLE_HDR*)"; emit_lvalue(e.args.at(0)); m_of << ".META)->align"; if( ! ty.m_data.is_TraitObject() ) { m_of << " )"; } } else { MIR_BUG(mir_res, "Unknown inner unsized type " << inner_ty << " for " << ty); } #else switch( metadata_type(ty) ) { case MetadataType::None: m_of << "__alignof__("; emit_ctype(ty); m_of << ")"; break; case MetadataType::Slice: { // TODO: Have a function that fetches the inner type for types like `Path` or `str` const auto& ity = *ty.m_data.as_Slice().inner; m_of << "__alignof__("; emit_ctype(ity); m_of << ")"; break; } case MetadataType::TraitObject: m_of << "((VTABLE_HDR*)"; emit_lvalue(e.args.at(0)); m_of << ".META)->align"; break; } #endif } else if( name == "type_id" ) { const auto& ty = params.m_types.at(0); // NOTE: Would define the typeid here, but it has to be public emit_lvalue(e.ret_val); m_of << " = (uintptr_t)&__typeid_" << Trans_Mangle(ty); } else if( name == "type_name" ) { auto s = FMT(params.m_types.at(0)); emit_lvalue(e.ret_val); m_of << ".PTR = \"" << FmtEscaped(s) << "\";\n\t"; emit_lvalue(e.ret_val); m_of << ".META = " << s.size() << ""; } else if( name == "transmute" ) { m_of << "memcpy( &"; emit_lvalue(e.ret_val); m_of << ", &"; emit_lvalue(e.args.at(0)); m_of << ", sizeof("; emit_ctype(params.m_types.at(0)); m_of << "))"; } else if( name == "copy_nonoverlapping" || name == "copy" ) { if( name == "copy" ) { m_of << "memmove"; } else { m_of << "memcpy"; } // 0: Source, 1: Destination, 2: Count m_of << "( "; emit_lvalue(e.args.at(1)); m_of << ", "; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(2)); m_of << " * sizeof("; emit_ctype(params.m_types.at(0)); m_of << ")"; m_of << ")"; } else if( name == "write_bytes" ) { // 0: Destination, 1: Value, 2: Count m_of << "memset( "; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", "; emit_lvalue(e.args.at(2)); m_of << " * sizeof("; emit_ctype(params.m_types.at(0)); m_of << ")"; m_of << ")"; } else if( name == "forget" ) { // Nothing needs to be done, this just stops the destructor from running. } else if( name == "drop_in_place" ) { emit_destructor_call( ::MIR::LValue::make_Deref({ box$(e.args.at(0).clone()) }), params.m_types.at(0), true ); } else if( name == "needs_drop" ) { // Returns `true` if the actual type given as `T` requires drop glue; // returns `false` if the actual type provided for `T` implements `Copy`. (Either otherwise) const auto& ty = params.m_types.at(0); emit_lvalue(e.ret_val); m_of << " = "; if( m_resolve.type_is_copy(Span(), ty) ) { m_of << "false"; } // If T: !Copy, return true else { m_of << "true"; } } else if( name == "uninit" ) { // Do nothing, leaves the destination undefined } else if( name == "init" ) { m_of << "memset( &"; emit_lvalue(e.ret_val); m_of << ", 0, sizeof("; emit_ctype(params.m_types.at(0)); m_of << "))"; } else if( name == "move_val_init" ) { m_of << "*"; emit_lvalue(e.args.at(0)); m_of << " = "; emit_lvalue(e.args.at(1)); } else if( name == "abort" ) { m_of << "abort()"; } else if( name == "try" ) { emit_lvalue(e.args.at(0)); m_of << "("; emit_lvalue(e.args.at(1)); m_of << ")"; } else if( name == "offset" ) { emit_lvalue(e.ret_val); m_of << " = "; emit_lvalue(e.args.at(0)); m_of << " + "; emit_lvalue(e.args.at(1)); } else if( name == "arith_offset" ) { emit_lvalue(e.ret_val); m_of << " = "; emit_lvalue(e.args.at(0)); m_of << " + "; emit_lvalue(e.args.at(1)); } else if( name == "bswap" ) { const auto& ty = params.m_types.at(0); MIR_ASSERT(mir_res, ty.m_data.is_Primitive(), "Invalid type passed to bwsap, must be a primitive, got " << ty); switch( ty.m_data.as_Primitive() ) { case ::HIR::CoreType::U8: emit_lvalue(e.ret_val); m_of << " = "; emit_lvalue(e.args.at(0)); break; case ::HIR::CoreType::U16: emit_lvalue(e.ret_val); m_of << " = __builtin_bswap16("; emit_lvalue(e.args.at(0)); m_of << ")"; break; case ::HIR::CoreType::U32: emit_lvalue(e.ret_val); m_of << " = __builtin_bswap32("; emit_lvalue(e.args.at(0)); m_of << ")"; break; case ::HIR::CoreType::U64: emit_lvalue(e.ret_val); m_of << " = __builtin_bswap64("; emit_lvalue(e.args.at(0)); m_of << ")"; break; case ::HIR::CoreType::U128: emit_lvalue(e.ret_val); m_of << " = __builtin_bswap128("; emit_lvalue(e.args.at(0)); m_of << ")"; break; default: MIR_TODO(mir_res, "bswap<" << ty << ">"); } } // > Obtain the discriminane of a &T as u64 else if( name == "discriminant_value" ) { const auto& ty = params.m_types.at(0); emit_lvalue(e.ret_val); m_of << " = "; if( ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum() ) { auto it = m_enum_repr_cache.find( ty.m_data.as_Path().path.m_data.as_Generic() ); if( it != m_enum_repr_cache.end() ) { emit_lvalue(e.args.at(0)); emit_nonzero_path(it->second); m_of << " != 0"; } else { emit_lvalue(e.args.at(0)); m_of << "->TAG"; } } else { m_of << "0"; } } // Hints else if( name == "unreachable" ) { m_of << "__builtin_unreachable()"; } else if( name == "assume" ) { // I don't assume :) } else if( name == "likely" ) { } else if( name == "unlikely" ) { } // Overflowing Arithmatic // HACK: Uses GCC intrinsics else if( name == "add_with_overflow" ) { emit_lvalue(e.ret_val); m_of << "._1 = __builtin_add_overflow("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", &"; emit_lvalue(e.ret_val); m_of << "._0)"; } else if( name == "sub_with_overflow" ) { emit_lvalue(e.ret_val); m_of << "._1 = __builtin_sub_overflow("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", &"; emit_lvalue(e.ret_val); m_of << "._0)"; } else if( name == "mul_with_overflow" ) { emit_lvalue(e.ret_val); m_of << "._1 = __builtin_mul_overflow("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", &"; emit_lvalue(e.ret_val); m_of << "._0)"; } else if( name == "overflowing_add" ) { m_of << "__builtin_add_overflow("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", &"; emit_lvalue(e.ret_val); m_of << ")"; } else if( name == "overflowing_sub" ) { m_of << "__builtin_sub_overflow("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", &"; emit_lvalue(e.ret_val); m_of << ")"; } else if( name == "overflowing_mul" ) { m_of << "__builtin_mul_overflow("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", &"; emit_lvalue(e.ret_val); m_of << ")"; } // Unchecked Arithmatic else if( name == "unchecked_div" ) { emit_lvalue(e.ret_val); m_of << " = "; emit_lvalue(e.args.at(0)); m_of << " / "; emit_lvalue(e.args.at(1)); } else if( name == "unchecked_rem" ) { emit_lvalue(e.ret_val); m_of << " = "; emit_lvalue(e.args.at(0)); m_of << " % "; emit_lvalue(e.args.at(1)); } // Bit Twiddling // - CounT Leading Zeroes // - CounT Trailing Zeroes else if( name == "ctlz" || name == "cttz" ) { auto emit_arg0 = [&](){ emit_lvalue(e.args.at(0)); }; const auto& ty = params.m_types.at(0); emit_lvalue(e.ret_val); m_of << " = ("; emit_lvalue(e.args.at(0)); m_of << " != 0 ? "; if( ty == ::HIR::CoreType::U128 ) { if( name == "ctlz" ) { m_of << "__builtin_clz128("; emit_lvalue(e.args.at(0)); m_of << ")"; } else { m_of << "__builtin_ctz128("; emit_lvalue(e.args.at(0)); m_of << ")"; } } else if( ty == ::HIR::CoreType::U64 || (ty == ::HIR::CoreType::Usize /*&& target_is_64_bit */) ) { if( name == "ctlz" ) { m_of << "__builtin_clz64("; emit_arg0(); m_of << ")"; } else { m_of << "__builtin_ctz64("; emit_arg0(); m_of << ")"; } } else { if( name == "ctlz" ) { m_of << "__builtin_clz("; emit_lvalue(e.args.at(0)); m_of << ")"; } else { m_of << "__builtin_ctz("; emit_lvalue(e.args.at(0)); m_of << ")"; } } m_of << " : sizeof("; emit_ctype(ty); m_of << ")*8)"; } // - CounT POPulated else if( name == "ctpop" ) { emit_lvalue(e.ret_val); m_of << " = __builtin_popcount("; emit_lvalue(e.args.at(0)); m_of << ")"; } // --- Floating Point // > Round to nearest integer, half-way rounds away from zero else if( name == "roundf32" && name == "roundf64" ) { emit_lvalue(e.ret_val); m_of << " = round" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "fabsf32" || name == "fabsf64" ) { emit_lvalue(e.ret_val); m_of << " = fabs" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "copysignf32" || name == "copysignf64" ) { emit_lvalue(e.ret_val); m_of << " = copysign" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ")"; } // > Returns the integer part of an `f32`. else if( name == "truncf32" || name == "truncf64" ) { emit_lvalue(e.ret_val); m_of << " = trunc" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "powif32" || name == "powif64" ) { emit_lvalue(e.ret_val); m_of << " = pow" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ")"; } else if( name == "powf32" || name == "powf64" ) { emit_lvalue(e.ret_val); m_of << " = pow" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ")"; } else if( name == "expf32" || name == "expf64" ) { emit_lvalue(e.ret_val); m_of << " = exp" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "exp2f32" || name == "exp2f64" ) { emit_lvalue(e.ret_val); m_of << " = exp2" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "logf32" || name == "logf64" ) { emit_lvalue(e.ret_val); m_of << " = log" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "log10f32" || name == "log10f64" ) { emit_lvalue(e.ret_val); m_of << " = log10" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "log2f32" || name == "log2f64" ) { emit_lvalue(e.ret_val); m_of << " = log2" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "sqrtf32" || name == "sqrtf64" ) { emit_lvalue(e.ret_val); m_of << " = sqrt" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "ceilf32" || name == "ceilf64" ) { emit_lvalue(e.ret_val); m_of << " = ceil" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "floorf32" || name == "floorf64" ) { emit_lvalue(e.ret_val); m_of << " = floor" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "roundf32" || name == "roundf64" ) { emit_lvalue(e.ret_val); m_of << " = round" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "cosf32" || name == "cosf64" ) { emit_lvalue(e.ret_val); m_of << " = cos" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "sinf32" || name == "sinf64" ) { emit_lvalue(e.ret_val); m_of << " = sin" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ")"; } else if( name == "fmaf32" || name == "fmaf64" ) { emit_lvalue(e.ret_val); m_of << " = fma" << (name.back()=='2'?"f":"") << "("; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ")"; } // --- Atomics! // > Single-ordering atomics else if( name == "atomic_xadd" || name.compare(0, 7+4+1, "atomic_xadd_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+4+1); emit_atomic_arith("fetch_add", ordering); } else if( name == "atomic_xsub" || name.compare(0, 7+4+1, "atomic_xsub_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+4+1); emit_atomic_arith("fetch_sub", ordering); } else if( name == "atomic_and" || name.compare(0, 7+3+1, "atomic_and_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+3+1); emit_atomic_arith("fetch_and", ordering); } else if( name == "atomic_or" || name.compare(0, 7+2+1, "atomic_or_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+2+1); emit_atomic_arith("fetch_or", ordering); } else if( name == "atomic_xor" || name.compare(0, 7+3+1, "atomic_xor_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+3+1); emit_atomic_arith("fetch_xor", ordering); } else if( name == "atomic_load" || name.compare(0, 7+4+1, "atomic_load_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+4+1); emit_lvalue(e.ret_val); m_of << " = atomic_load_explicit((_Atomic "; emit_ctype(params.m_types.at(0)); m_of << "*)"; emit_lvalue(e.args.at(0)); m_of << ", " << ordering << ")"; } else if( name == "atomic_store" || name.compare(0, 7+5+1, "atomic_store_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+5+1); m_of << "atomic_store_explicit((_Atomic "; emit_ctype(params.m_types.at(0)); m_of << "*)"; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", " << ordering << ")"; } // Comare+Exchange (has two orderings) else if( name == "atomic_cxchg_acq_failrelaxed" ) { emit_atomic_cxchg(e, "memory_order_acquire", "memory_order_relaxed", false); } else if( name == "atomic_cxchg_acqrel_failrelaxed" ) { emit_atomic_cxchg(e, "memory_order_acq_rel", "memory_order_relaxed", false); } // _rel = Release, Relaxed (not Release,Release) else if( name == "atomic_cxchg_rel" ) { emit_atomic_cxchg(e, "memory_order_release", "memory_order_relaxed", false); } // _acqrel = Release, Acquire (not AcqRel,AcqRel) else if( name == "atomic_cxchg_acqrel" ) { emit_atomic_cxchg(e, "memory_order_acq_rel", "memory_order_acquire", false); } else if( name.compare(0, 7+6+4, "atomic_cxchg_fail") == 0 ) { auto fail_ordering = H::get_atomic_ordering(mir_res, name, 7+6+4); emit_atomic_cxchg(e, "memory_order_seq_cst", fail_ordering, false); } else if( name == "atomic_cxchg" || name.compare(0, 7+6, "atomic_cxchg_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+6); emit_atomic_cxchg(e, ordering, ordering, false); } else if( name == "atomic_cxchgweak_acq_failrelaxed" ) { emit_atomic_cxchg(e, "memory_order_acquire", "memory_order_relaxed", true); } else if( name == "atomic_cxchgweak_acqrel_failrelaxed" ) { emit_atomic_cxchg(e, "memory_order_acq_rel", "memory_order_relaxed", true); } else if( name.compare(0, 7+10+4, "atomic_cxchgweak_fail") == 0 ) { auto fail_ordering = H::get_atomic_ordering(mir_res, name, 7+10+4); emit_atomic_cxchg(e, "memory_order_seq_cst", fail_ordering, true); } else if( name == "atomic_cxchgweak" || name.compare(0, 7+9+1, "atomic_cxchgweak_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+9+1); emit_atomic_cxchg(e, ordering, ordering, true); } else if( name == "atomic_xchg" || name.compare(0, 7+5, "atomic_xchg_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+5); emit_lvalue(e.ret_val); m_of << " = atomic_exchange_explicit((_Atomic "; emit_ctype(params.m_types.at(0)); m_of << "*)"; emit_lvalue(e.args.at(0)); m_of << ", "; emit_lvalue(e.args.at(1)); m_of << ", " << ordering << ")"; } else if( name == "atomic_fence" || name.compare(0, 7+6, "atomic_fence_") == 0 ) { auto ordering = H::get_atomic_ordering(mir_res, name, 7+6); m_of << "atomic_thread_fence(" << ordering << ")"; } else { MIR_BUG(mir_res, "Unknown intrinsic '" << name << "'"); } m_of << ";\n"; } void emit_destructor_call(const ::MIR::LValue& slot, const ::HIR::TypeRef& ty, bool unsized_valid) { TU_MATCHA( (ty.m_data), (te), // Impossible (Diverge, ), (Infer, ), (ErasedType, ), (Closure, ), (Generic, ), // Nothing (Primitive, ), (Pointer, ), (Function, ), // Has drop glue/destructors (Borrow, if( te.type == ::HIR::BorrowType::Owned ) { // Call drop glue on inner. emit_destructor_call( ::MIR::LValue::make_Deref({ box$(slot.clone()) }), *te.inner, true ); } ), (Path, // Call drop glue // - TODO: If the destructor is known to do nothing, don't call it. auto p = ::HIR::Path(ty.clone(), "#drop_glue"); const char* make_fcn = nullptr; switch( metadata_type(ty) ) { case MetadataType::None: m_of << "\t" << Trans_Mangle(p) << "(&"; emit_lvalue(slot); m_of << ");\n"; break; case MetadataType::Slice: make_fcn = "make_sliceptr"; if(0) case MetadataType::TraitObject: make_fcn = "make_traitobjptr"; m_of << "\t" << Trans_Mangle(p) << "( " << make_fcn << "(&"; emit_lvalue(slot); m_of << ", "; const auto* lvp = &slot; while(const auto* le = lvp->opt_Field()) lvp = &*le->val; MIR_ASSERT(*m_mir_res, lvp->is_Deref(), "Access to unized type without a deref - " << *lvp << " (part of " << slot << ")"); emit_lvalue(*lvp->as_Deref().val); m_of << ".META"; m_of << ") );\n"; break; } ), (Array, // Emit destructors for all entries if( te.size_val > 0 ) { ::MIR::LValue lv = ::MIR::LValue::make_Field({ box$(slot.clone()), 0 }); for(unsigned int i = 0; i < te.size_val; i ++) { lv.as_Field().field_index = i; emit_destructor_call(lv, *te.inner, false); } } ), (Tuple, // Emit destructors for all entries if( te.size() > 0 ) { ::MIR::LValue lv = ::MIR::LValue::make_Field({ box$(slot.clone()), 0 }); for(unsigned int i = 0; i < te.size(); i ++) { lv.as_Field().field_index = i; emit_destructor_call(lv, te[i], unsized_valid && (i == te.size()-1)); } } ), (TraitObject, MIR_ASSERT(*m_mir_res, unsized_valid, "Dropping TraitObject without a pointer"); //MIR_ASSERT(*m_mir_res, slot.is_Deref(), "Dropping a TraitObject through a non-Deref"); // Call destructor in vtable ), (Slice, MIR_ASSERT(*m_mir_res, unsized_valid, "Dropping Slice without a pointer"); //MIR_ASSERT(*m_mir_res, slot.is_Deref(), "Dropping a slice through a non-Deref"); // Call destructor on all entries ) ) } const ::HIR::Literal& get_literal_for_const(const ::HIR::Path& path) { TU_MATCHA( (path.m_data), (pe), (Generic, if( pe.m_params.m_types.size() > 0 ) MIR_TODO(*m_mir_res, "get_literal_for_const - Paths with generics " << path); return m_crate.get_constant_by_path(Span(), pe.m_path).m_value_res; ), (UfcsUnknown, MIR_BUG(*m_mir_res, "get_literal_for_const - UfcsUnknown " << path); ), (UfcsKnown, MIR_TODO(*m_mir_res, "get_literal_for_const - UfcsKnown " << path); ), (UfcsInherent, MIR_TODO(*m_mir_res, "get_literal_for_const - UfcsInherent " << path); ) ) throw ""; } void assign_from_literal(::std::function emit_dst, const ::HIR::TypeRef& ty, const ::HIR::Literal& lit) { //TRACE_FUNCTION_F("ty=" << ty << ", lit=" << lit); Span sp; ::HIR::TypeRef tmp; auto monomorph_with = [&](const ::HIR::PathParams& pp, const ::HIR::TypeRef& ty)->const ::HIR::TypeRef& { if( monomorphise_type_needed(ty) ) { tmp = monomorphise_type_with(sp, ty, monomorphise_type_get_cb(sp, nullptr, &pp, nullptr), false); m_resolve.expand_associated_types(sp, tmp); return tmp; } else { return ty; } }; auto get_inner_type = [&](unsigned int var, unsigned int idx)->const ::HIR::TypeRef& { TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Array, te, return *te.inner; ) else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Path, te, const auto& pp = te.path.m_data.as_Generic().m_params; TU_MATCHA((te.binding), (pbe), (Unbound, MIR_BUG(*m_mir_res, "Unbound type path " << ty); ), (Opaque, MIR_BUG(*m_mir_res, "Opaque type path " << ty); ), (Struct, TU_MATCHA( (pbe->m_data), (se), (Unit, MIR_BUG(*m_mir_res, "Unit struct " << ty); ), (Tuple, return monomorph_with(pp, se.at(idx).ent); ), (Named, return monomorph_with(pp, se.at(idx).second.ent); ) ) ), (Union, MIR_TODO(*m_mir_res, "Union literals"); ), (Enum, const auto& evar = pbe->m_variants.at(var); TU_MATCHA( (evar.second), (se), (Unit, MIR_BUG(*m_mir_res, "Unit enum var " << ty << " #" << var << " - fld " << idx); ), (Value, MIR_BUG(*m_mir_res, "Value enum var " << ty << " #" << var << " - fld " << idx); ), (Tuple, return monomorph_with(pp, se.at(idx).ent); ), (Struct, return monomorph_with(pp, se.at(idx).second.ent); ) ) ) ) throw ""; ) else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Tuple, te, return te.at(idx); ) else { MIR_TODO(*m_mir_res, "Unknown type in list literal - " << ty); } }; TU_MATCHA( (lit), (e), (Invalid, m_of << "/* INVALID */"; ), (List, if( ty.m_data.is_Array() ) { for(unsigned int i = 0; i < e.size(); i ++) { if(i != 0) m_of << ";\n\t"; assign_from_literal([&](){ emit_dst(); m_of << ".DATA[" << i << "]"; }, *ty.m_data.as_Array().inner, e[i]); } } else { for(unsigned int i = 0; i < e.size(); i ++) { if(i != 0) m_of << ";\n\t"; assign_from_literal([&](){ emit_dst(); m_of << "._" << i; }, get_inner_type(0, i), e[i]); } } ), (Variant, MIR_ASSERT(*m_mir_res, ty.m_data.is_Path(), ""); MIR_ASSERT(*m_mir_res, ty.m_data.as_Path().binding.is_Enum(), ""); const auto& enm = *ty.m_data.as_Path().binding.as_Enum(); auto it = m_enum_repr_cache.find(ty.m_data.as_Path().path.m_data.as_Generic()); if( it != m_enum_repr_cache.end() ) { if( e.idx == 0 ) { emit_nonzero_path(it->second); m_of << " = 0"; } else { assign_from_literal([&](){ emit_dst(); m_of << "._0"; }, get_inner_type(e.idx, 0), e.vals[0]); } } else if( enm.m_repr != ::HIR::Enum::Repr::Rust || ::std::all_of(enm.m_variants.begin(), enm.m_variants.end(), [](const auto& x){return x.second.is_Unit() || x.second.is_Value();}) ) { MIR_ASSERT(*m_mir_res, e.vals.empty(), "Value-only enum with fields"); emit_dst(); m_of << ".TAG = " << e.idx; } else { emit_dst(); m_of << ".TAG = " << e.idx; for(unsigned int i = 0; i < e.vals.size(); i ++) { m_of << ";\n\t"; assign_from_literal([&](){ emit_dst(); m_of << ".DATA.var_" << e.idx << "._" << i; }, get_inner_type(e.idx, i), e.vals[i]); } } ), (Integer, emit_dst(); m_of << " = "; emit_literal(ty, lit, {}); ), (Float, emit_dst(); m_of << " = " << e; ), (BorrowOf, if( ty.m_data.is_Function() ) { emit_dst(); m_of << " = " << Trans_Mangle(e); } else if( ty.m_data.is_Borrow() ) { const auto& ity = *ty.m_data.as_Borrow().inner; switch( metadata_type(ity) ) { case MetadataType::None: emit_dst(); m_of << " = &" << Trans_Mangle(e); break; case MetadataType::Slice: emit_dst(); m_of << ".PTR = &" << Trans_Mangle(e) << ";\n\t"; // HACK: Since getting the size is hard, use two sizeofs emit_dst(); m_of << ".META = sizeof(" << Trans_Mangle(e) << ") / "; if( ity.m_data.is_Slice() ) { m_of << "sizeof("; emit_ctype(*ity.m_data.as_Slice().inner); m_of << ")"; } else { m_of << "/*TODO*/"; } break; case MetadataType::TraitObject: emit_dst(); m_of << ".PTR = &" << Trans_Mangle(e) << ";\n\t"; emit_dst(); m_of << ".META = /* TODO: Const VTable */"; break; } } else { emit_dst(); m_of << " = &" << Trans_Mangle(e); } ), (String, emit_dst(); m_of << ".PTR = "; m_of << "\"" << ::std::oct; for(const auto& v : e) { if( ' ' <= v && v < 0x7F && v != '"' && v != '\\' ) m_of << v; else m_of << "\\" << (unsigned int)v; } m_of << "\"" << ::std::dec; m_of << ";\n\t"; emit_dst(); m_of << ".META = " << e.size(); ) ) } void emit_lvalue(const ::MIR::LValue& val) { TU_MATCHA( (val), (e), (Variable, m_of << "var" << e; ), (Temporary, m_of << "tmp" << e.idx; ), (Argument, m_of << "arg" << e.idx; ), (Return, m_of << "rv"; ), (Static, m_of << Trans_Mangle(e); ), (Field, ::HIR::TypeRef tmp; const auto& ty = m_mir_res->get_lvalue_type(tmp, *e.val); if( ty.m_data.is_Slice() ) { if( e.val->is_Deref() ) { m_of << "(("; emit_ctype(*ty.m_data.as_Slice().inner); m_of << "*)"; emit_lvalue(*e.val->as_Deref().val); m_of << ".PTR)"; } else { emit_lvalue(*e.val); } m_of << "[" << e.field_index << "]"; } else if( ty.m_data.is_Array() ) { emit_lvalue(*e.val); m_of << ".DATA[" << e.field_index << "]"; } else if( e.val->is_Deref() ) { auto dst_type = metadata_type(ty); if( dst_type != MetadataType::None ) { m_of << "(("; emit_ctype(ty); m_of << "*)"; emit_lvalue(*e.val->as_Deref().val); m_of << ".PTR)->_" << e.field_index; } else { emit_lvalue(*e.val->as_Deref().val); m_of << "->_" << e.field_index; } } else { emit_lvalue(*e.val); m_of << "._" << e.field_index; } ), (Deref, // TODO: If the type is unsized, then this pointer is a fat pointer, so we need to cast the data pointer. ::HIR::TypeRef tmp; const auto& ty = m_mir_res->get_lvalue_type(tmp, val); auto dst_type = metadata_type(ty); if( dst_type != MetadataType:: None ) { m_of << "(*("; emit_ctype(ty); m_of << "*)"; emit_lvalue(*e.val); m_of << ")"; } else { m_of << "(*"; emit_lvalue(*e.val); m_of << ")"; } ), (Index, ::HIR::TypeRef tmp; const auto& ty = m_mir_res->get_lvalue_type(tmp, *e.val); m_of << "("; if( ty.m_data.is_Slice() ) { if( e.val->is_Deref() ) { m_of << "("; emit_ctype(*ty.m_data.as_Slice().inner); m_of << "*)"; emit_lvalue(*e.val->as_Deref().val); m_of << ".PTR"; } else { emit_lvalue(*e.val); } } else if( ty.m_data.is_Array() ) { emit_lvalue(*e.val); m_of << ".DATA"; } else { emit_lvalue(*e.val); } m_of << ")["; emit_lvalue(*e.idx); m_of << "]"; ), (Downcast, ::HIR::TypeRef tmp; const auto& ty = m_mir_res->get_lvalue_type(tmp, *e.val); emit_lvalue(*e.val); MIR_ASSERT(*m_mir_res, ty.m_data.is_Path(), "Downcast on non-Path type - " << ty); if( ty.m_data.as_Path().binding.is_Enum() ) { auto it = m_enum_repr_cache.find(ty.m_data.as_Path().path.m_data.as_Generic()); if( it != m_enum_repr_cache.end() ) { MIR_ASSERT(*m_mir_res, e.variant_index == 1, ""); // NOTE: Downcast returns a magic tuple //m_of << "._0"; break ; } else { m_of << ".DATA"; } } m_of << ".var_" << e.variant_index; ) ) } void emit_ctype(const ::HIR::TypeRef& ty) { emit_ctype(ty, FMT_CB(_,)); } void emit_ctype(const ::HIR::TypeRef& ty, ::FmtLambda inner) { TU_MATCHA( (ty.m_data), (te), (Infer, m_of << "@" << ty << "@" << inner; ), (Diverge, m_of << "tBANG " << inner; ), (Primitive, switch(te) { case ::HIR::CoreType::Usize: m_of << "uintptr_t"; break; case ::HIR::CoreType::Isize: m_of << "intptr_t"; break; case ::HIR::CoreType::U8: m_of << "uint8_t"; break; case ::HIR::CoreType::I8: m_of << "int8_t"; break; case ::HIR::CoreType::U16: m_of << "uint16_t"; break; case ::HIR::CoreType::I16: m_of << "int16_t"; break; case ::HIR::CoreType::U32: m_of << "uint32_t"; break; case ::HIR::CoreType::I32: m_of << "int32_t"; break; case ::HIR::CoreType::U64: m_of << "uint64_t"; break; case ::HIR::CoreType::I64: m_of << "int64_t"; break; case ::HIR::CoreType::U128: m_of << "unsigned __int128"; break; case ::HIR::CoreType::I128: m_of << "__int128"; break; case ::HIR::CoreType::F32: m_of << "float"; break; case ::HIR::CoreType::F64: m_of << "double"; break; case ::HIR::CoreType::Bool: m_of << "bool"; break; case ::HIR::CoreType::Char: m_of << "CHAR"; break; case ::HIR::CoreType::Str: MIR_BUG(*m_mir_res, "Raw str"); } m_of << " " << inner; ), (Path, //if( const auto* ity = m_resolve.is_type_owned_box(ty) ) { // emit_ctype_ptr(*ity, inner); // return ; //} TU_MATCHA( (te.binding), (tpb), (Struct, m_of << "struct s_" << Trans_Mangle(te.path); ), (Union, m_of << "union u_" << Trans_Mangle(te.path); ), (Enum, m_of << "struct e_" << Trans_Mangle(te.path); ), (Unbound, MIR_BUG(*m_mir_res, "Unbound type path in trans - " << ty); ), (Opaque, MIR_BUG(*m_mir_res, "Opaque path in trans - " << ty); ) ) m_of << " " << inner; ), (Generic, MIR_BUG(*m_mir_res, "Generic in trans - " << ty); ), (TraitObject, MIR_BUG(*m_mir_res, "Raw trait object - " << ty); ), (ErasedType, MIR_BUG(*m_mir_res, "ErasedType in trans - " << ty); ), (Array, m_of << "t_" << Trans_Mangle(ty) << " " << inner; //emit_ctype(*te.inner, inner); //m_of << "[" << te.size_val << "]"; ), (Slice, MIR_BUG(*m_mir_res, "Raw slice object - " << ty); ), (Tuple, if( te.size() == 0 ) m_of << "tUNIT"; else { m_of << "TUP_" << te.size(); for(const auto& t : te) m_of << "_" << Trans_Mangle(t); } m_of << " " << inner; ), (Borrow, emit_ctype_ptr(*te.inner, inner); ), (Pointer, emit_ctype_ptr(*te.inner, inner); ), (Function, m_of << "t_" << Trans_Mangle(ty) << " " << inner; ), (Closure, MIR_BUG(*m_mir_res, "Closure during trans - " << ty); ) ) } ::HIR::TypeRef get_inner_unsized_type(const ::HIR::TypeRef& ty) { if( ty == ::HIR::CoreType::Str || ty.m_data.is_Slice() ) { return ty.clone(); } else if( ty.m_data.is_TraitObject() ) { return ty.clone(); } else if( ty.m_data.is_Path() ) { const ::HIR::TraitMarkings* markings; TU_MATCH_DEF( ::HIR::TypeRef::TypePathBinding, (ty.m_data.as_Path().binding), (tpb), ( MIR_BUG(*m_mir_res, "Unbound/opaque path in trans - " << ty); ), (Struct, markings = &tpb->m_markings; ), (Union, markings = &tpb->m_markings; ), (Enum, markings = &tpb->m_markings; ) ) switch( markings->dst_type ) { case ::HIR::TraitMarkings::DstType::None: return ::HIR::TypeRef(); case ::HIR::TraitMarkings::DstType::Slice: case ::HIR::TraitMarkings::DstType::TraitObject: case ::HIR::TraitMarkings::DstType::Possible: { // TODO: How to figure out? Lazy way is to check the monomorpised type of the last field (structs only) const auto& path = ty.m_data.as_Path().path.m_data.as_Generic(); const auto& str = *ty.m_data.as_Path().binding.as_Struct(); auto monomorph = [&](const auto& tpl) { // TODO: expand_associated_types auto rv = monomorphise_type(sp, str.m_params, path.m_params, tpl); m_resolve.expand_associated_types(sp, rv); return rv; }; TU_MATCHA( (str.m_data), (se), (Unit, MIR_BUG(*m_mir_res, "Unit-like struct with DstType::Possible"); ), (Tuple, return get_inner_unsized_type( monomorph(se.back().ent) ); ), (Named, return get_inner_unsized_type( monomorph(se.back().second.ent) ); ) ) throw ""; } } throw ""; } else { return ::HIR::TypeRef(); } } // TODO: Move this to a more common location MetadataType metadata_type(const ::HIR::TypeRef& ty) const { if( ty == ::HIR::CoreType::Str || ty.m_data.is_Slice() ) { return MetadataType::Slice; } else if( ty.m_data.is_TraitObject() ) { return MetadataType::TraitObject; } else if( ty.m_data.is_Path() ) { const ::HIR::TraitMarkings* markings; TU_MATCH_DEF( ::HIR::TypeRef::TypePathBinding, (ty.m_data.as_Path().binding), (tpb), ( MIR_BUG(*m_mir_res, "Unbound/opaque path in trans - " << ty); ), (Struct, markings = &tpb->m_markings; ), (Union, markings = &tpb->m_markings; ), (Enum, markings = &tpb->m_markings; ) ) switch( markings->dst_type ) { case ::HIR::TraitMarkings::DstType::None: return MetadataType::None; case ::HIR::TraitMarkings::DstType::Possible: { // TODO: How to figure out? Lazy way is to check the monomorpised type of the last field (structs only) const auto& path = ty.m_data.as_Path().path.m_data.as_Generic(); const auto& str = *ty.m_data.as_Path().binding.as_Struct(); auto monomorph = [&](const auto& tpl) { auto rv = monomorphise_type(sp, str.m_params, path.m_params, tpl); m_resolve.expand_associated_types(sp, rv); return rv; }; TU_MATCHA( (str.m_data), (se), (Unit, MIR_BUG(*m_mir_res, "Unit-like struct with DstType::Possible"); ), (Tuple, return metadata_type( monomorph(se.back().ent) ); ), (Named, return metadata_type( monomorph(se.back().second.ent) ); ) ) //MIR_TODO(*m_mir_res, "Determine DST type when ::Possible - " << ty); return MetadataType::None; } case ::HIR::TraitMarkings::DstType::Slice: return MetadataType::Slice; case ::HIR::TraitMarkings::DstType::TraitObject: return MetadataType::TraitObject; } throw ""; } else { return MetadataType::None; } } void emit_ctype_ptr(const ::HIR::TypeRef& inner_ty, ::FmtLambda inner) { //if( inner_ty.m_data.is_Array() ) { // emit_ctype(inner_ty, FMT_CB(ss, ss << "(*" << inner << ")";)); //} //else { switch( metadata_type(inner_ty) ) { case MetadataType::None: emit_ctype(inner_ty, FMT_CB(ss, ss << "*" << inner;)); break; case MetadataType::Slice: m_of << "SLICE_PTR " << inner; break; case MetadataType::TraitObject: m_of << "TRAITOBJ_PTR " << inner; break; } } } bool is_dst(const ::HIR::TypeRef& ty) const { return metadata_type(ty) != MetadataType::None; } }; Span CodeGenerator_C::sp; } ::std::unique_ptr Trans_Codegen_GetGeneratorC(const ::HIR::Crate& crate, const ::std::string& outfile) { return ::std::unique_ptr(new CodeGenerator_C(crate, outfile)); }