diff options
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | docs/target.md | 50 | ||||
-rw-r--r-- | src/mir/optimise.cpp | 470 | ||||
-rw-r--r-- | src/trans/codegen_c.cpp | 24 | ||||
-rw-r--r-- | src/trans/target.cpp | 67 | ||||
-rw-r--r-- | tools/common/toml.h | 15 |
6 files changed, 390 insertions, 250 deletions
@@ -46,6 +46,18 @@ Windows - Run `vsproject/build_rustc_minicargo.cmd` to attempt to build libstd +Building non-rustc code +======================= + +To build your own code with mrustc, first you need to build at least libcore (and probably the full standard library). +This can be done on linux by running `make -f minicargo.mk LIBS`, or on windows with `build_std.cmd`. + +Next, run +- `minicargo -L <path_to_libstd> <crate_path>` to build a cargo project. +- or, `mrustc -L <path_to_libstd> --out-dir <output_directory> <path_to_main.rs>` to directly invoke mrustc. + +For additional options, both programs have a `--help` option. + Diagnosing Issues and Reporting Bugs ==================================== @@ -77,6 +89,8 @@ Current Features - Functional cargo clone (minicargo) - Includes build script support - Procedural macros (custom derive) +- Custom target specifications + - See `docs/target.md` Plans ===== diff --git a/docs/target.md b/docs/target.md new file mode 100644 index 00000000..6b5b088d --- /dev/null +++ b/docs/target.md @@ -0,0 +1,50 @@ + +Target Overrides +================ + +Mrustc supports a few targets out of the box (of varying levels of usablity), these are listed in +`src/trans/target.cpp`. If the built-in targets aren't suitable, a custom target can be specified with the help of a +custom target toml file. + +To specify a target when running `mrustc` (or `minicaro`), pass `--target <triple>` or `--target +./path/to/target.toml` (the presence of a slash, backwards or forwards, is what determines if the target is treated as +a custom target file. + + +Custom target format +-------------------- + +Recreation of the `arm-linux-gnu` target +```toml +[target] +family = "unix" +os-name = "linux" +env-name = "gnu" +arch = "arm" + +[backend.c] +variant = "gnu" +target = "arm-linux-gnu" +``` + +Recreation of the `i586-windows-gnu` target (with architecture) +```toml +[target] +family = "windows" +os-name = "windows" +env-name = "gnu" + +[backend.c] +variant = "gnu" +target = "mingw32" + +[arch] +name = "x86" +pointer-bits = 32 +is-big-endian = false +has-atomic-u8 = true +has-atomic-u16 = true +has-atomic-u32 = true +has-atomic-u64 = false +has-atomic-ptr = true +``` diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 2cc81258..ae1f3120 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -30,6 +30,246 @@ #define DUMP_AFTER_DONE 0 #define CHECK_AFTER_DONE 2 // 1 = Check before GC, 2 = check before and after GC +// ---- +// List of optimisations avaliable +// ---- +bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool minimal, const TransList* list=nullptr); +bool MIR_Optimise_SplitAggregates(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_PropagateKnownValues(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn); // Eliminate useless temporaries +bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_CommonStatements(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_DeadDropFlags(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_DeadAssignments(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_NoopRemoval(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_GarbageCollect_Partial(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn); + +/// A minimum set of optimisations: +/// - Inlines `#[inline(always)]` functions +/// - Simplifies the call graph (by removing chained gotos) +/// - Sorts blocks into a rough flow order +void MIR_OptimiseMin(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) +{ + static Span sp; + TRACE_FUNCTION_F(path); + ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; + + while( MIR_Optimise_Inlining(state, fcn, true) ) + { + MIR_Cleanup(resolve, path, fcn, args, ret_type); + //MIR_Dump_Fcn(::std::cout, fcn); + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + } + + MIR_Optimise_BlockSimplify(state, fcn); + MIR_Optimise_UnifyBlocks(state, fcn); + + //MIR_Optimise_GarbageCollect_Partial(state, fcn); + + MIR_Optimise_GarbageCollect(state, fcn); + //MIR_Validate_Full(resolve, path, fcn, args, ret_type); + MIR_SortBlocks(resolve, path, fcn); + +#if CHECK_AFTER_DONE > 1 + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + return ; +} +/// Perfom inlining only, using a list of monomorphised functions, then cleans up the flow graph +/// +/// Returns true if any optimisation was performed +bool MIR_OptimiseInline(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type, const TransList& list) +{ + static Span sp; + bool rv = false; + TRACE_FUNCTION_FR(path, rv); + ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; + + while( MIR_Optimise_Inlining(state, fcn, false, &list) ) + { + MIR_Cleanup(resolve, path, fcn, args, ret_type); +#if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + rv = true; + } + + if( rv ) + { + // - Constant propagation (inlining may have lead to some more constant information + MIR_Optimise_ConstPropagte(state, fcn); + // - Unify non-overlapping locals + if(MIR_Optimise_UnifyTemporaries(state, fcn)) + { +#if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + } + // - Remove no-op statements + MIR_Optimise_NoopRemoval(state, fcn); + + // - Simplify the CFG then unify equal blocks + MIR_Optimise_BlockSimplify(state, fcn); + MIR_Optimise_UnifyBlocks(state, fcn); + + // - Remove dead code + MIR_Optimise_GarbageCollect(state, fcn); + // - Sort blocks into a rough flow + MIR_SortBlocks(resolve, path, fcn); + +#if CHECK_AFTER_DONE > 1 + //MIR_Validate_Full(resolve, path, fcn, args, ret_type); + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + } + + return rv; +} +void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) +{ + static Span sp; + TRACE_FUNCTION_F(path); + ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; + + bool change_happened; + unsigned int pass_num = 0; + do + { + MIR_ASSERT(state, pass_num < 100, "Too many MIR optimisation iterations"); + + change_happened = false; + TRACE_FUNCTION_FR("Pass " << pass_num, change_happened); + + // >> Simplify call graph (removes gotos to blocks with a single use) + MIR_Optimise_BlockSimplify(state, fcn); + + // >> Apply known constants + change_happened |= MIR_Optimise_ConstPropagte(state, fcn); + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + + // Attempt to remove useless temporaries + while( MIR_Optimise_DeTemporary(state, fcn) ) + { + change_happened = true; + } +#if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + + // TODO: Split apart aggregates (just tuples?) where it's never used + // as an aggregate. (Written once, never used directly) + change_happened |= MIR_Optimise_SplitAggregates(state, fcn); + + // >> Replace values from composites if they're known + // - Undoes the inefficiencies from the `match (a, b) { ... }` pattern + change_happened |= MIR_Optimise_PropagateKnownValues(state, fcn); +#if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + + // TODO: Convert `&mut *mut_foo` into `mut_foo` if the source is movable and not used afterwards + +#if DUMP_BEFORE_ALL || DUMP_BEFORE_PSA + if( debug_enabled() ) MIR_Dump_Fcn(::std::cout, fcn); +#endif + // >> Propagate/remove dead assignments + while( MIR_Optimise_PropagateSingleAssignments(state, fcn) ) + change_happened = true; +#if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + + // >> Move common statements (assignments) across gotos. + change_happened |= MIR_Optimise_CommonStatements(state, fcn); + + // >> Combine Duplicate Blocks + change_happened |= MIR_Optimise_UnifyBlocks(state, fcn); + // >> Remove assignments of unsed drop flags + change_happened |= MIR_Optimise_DeadDropFlags(state, fcn); + // >> Remove assignments that are never read + change_happened |= MIR_Optimise_DeadAssignments(state, fcn); + // >> Remove no-op assignments + change_happened |= MIR_Optimise_NoopRemoval(state, fcn); + + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + + // >> Inline short functions + if( !change_happened ) + { + bool inline_happened = MIR_Optimise_Inlining(state, fcn, false); + if( inline_happened ) + { + // Apply cleanup again (as monomorpisation in inlining may have exposed a vtable call) + MIR_Cleanup(resolve, path, fcn, args, ret_type); + //MIR_Dump_Fcn(::std::cout, fcn); + change_happened = true; + } + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + } + + if( change_happened ) + { + #if DUMP_AFTER_PASS + if( debug_enabled() ) { + MIR_Dump_Fcn(::std::cout, fcn); + } + #endif + #if CHECK_AFTER_PASS && !CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + } + + MIR_Optimise_GarbageCollect_Partial(state, fcn); + pass_num += 1; + } while( change_happened ); + + // Run UnifyTemporaries last, then unify blocks, then run some + // optimisations that might be affected + if(MIR_Optimise_UnifyTemporaries(state, fcn)) + { +#if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif + MIR_Optimise_UnifyBlocks(state, fcn); + //MIR_Optimise_ConstPropagte(state, fcn); + MIR_Optimise_NoopRemoval(state, fcn); + } + + + #if DUMP_AFTER_DONE + if( debug_enabled() ) { + MIR_Dump_Fcn(::std::cout, fcn); + } + #endif + #if CHECK_AFTER_DONE + // DEFENCE: Run validation _before_ GC (so validation errors refer to the pre-gc numbers) + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + // GC pass on blocks and variables + // - Find unused blocks, then delete and rewrite all references. + MIR_Optimise_GarbageCollect(state, fcn); + + //MIR_Validate_Full(resolve, path, fcn, args, ret_type); + + MIR_SortBlocks(resolve, path, fcn); +#if CHECK_AFTER_DONE > 1 + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif +} + namespace { ::MIR::BasicBlockId get_new_target(const ::MIR::TypeResolve& state, ::MIR::BasicBlockId bb) { @@ -517,231 +757,6 @@ namespace { } } -// TODO: Move this block of definitions+code above the namespace above. - -bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool minimal, const TransList* list=nullptr); -bool MIR_Optimise_SplitAggregates(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_PropagateKnownValues(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn); // Eliminate useless temporaries -bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_CommonStatements(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_DeadDropFlags(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_DeadAssignments(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_NoopRemoval(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_GarbageCollect_Partial(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn); - -/// A minimum set of optimisations: -/// - Inlines `#[inline(always)]` functions -/// - Simplifies the call graph (by removing chained gotos) -/// - Sorts blocks into a rough flow order -void MIR_OptimiseMin(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) -{ - static Span sp; - TRACE_FUNCTION_F(path); - ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; - - while( MIR_Optimise_Inlining(state, fcn, true) ) - { - MIR_Cleanup(resolve, path, fcn, args, ret_type); - //MIR_Dump_Fcn(::std::cout, fcn); - #if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); - #endif - } - - MIR_Optimise_BlockSimplify(state, fcn); - MIR_Optimise_UnifyBlocks(state, fcn); - - //MIR_Optimise_GarbageCollect_Partial(state, fcn); - - MIR_Optimise_GarbageCollect(state, fcn); - //MIR_Validate_Full(resolve, path, fcn, args, ret_type); - MIR_SortBlocks(resolve, path, fcn); - -#if CHECK_AFTER_DONE > 1 - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif - return ; -} -/// Optimise doing inlining then cleaning up the mess -/// -/// Returns true if any optimisation was performed -/// -/// NOTE: This function can only be called after enumeration and monomorphisation, so it takes the TransList by reference not nullable pointer -bool MIR_OptimiseInline(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type, const TransList& list) -{ - static Span sp; - bool rv = false; - TRACE_FUNCTION_FR(path, rv); - ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; - - while( MIR_Optimise_Inlining(state, fcn, false, &list) ) - { - MIR_Cleanup(resolve, path, fcn, args, ret_type); -#if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif - rv = true; - } - - if( rv ) - { - MIR_Optimise_BlockSimplify(state, fcn); - MIR_Optimise_UnifyBlocks(state, fcn); - - MIR_Optimise_GarbageCollect(state, fcn); - //MIR_Validate_Full(resolve, path, fcn, args, ret_type); - MIR_SortBlocks(resolve, path, fcn); - -#if CHECK_AFTER_DONE > 1 - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif - } - - return rv; -} -void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) -{ - static Span sp; - TRACE_FUNCTION_F(path); - ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; - - bool change_happened; - unsigned int pass_num = 0; - do - { - MIR_ASSERT(state, pass_num < 100, "Too many MIR optimisation iterations"); - - change_happened = false; - TRACE_FUNCTION_FR("Pass " << pass_num, change_happened); - - // >> Simplify call graph (removes gotos to blocks with a single use) - MIR_Optimise_BlockSimplify(state, fcn); - - // >> Apply known constants - change_happened |= MIR_Optimise_ConstPropagte(state, fcn); - #if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); - #endif - - // Attempt to remove useless temporaries - while( MIR_Optimise_DeTemporary(state, fcn) ) - { - change_happened = true; - } -#if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif - - // TODO: Split apart aggregates (just tuples?) where it's never used - // as an aggregate. (Written once, never used directly) - change_happened |= MIR_Optimise_SplitAggregates(state, fcn); - - // >> Replace values from composites if they're known - // - Undoes the inefficiencies from the `match (a, b) { ... }` pattern - change_happened |= MIR_Optimise_PropagateKnownValues(state, fcn); -#if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif - - // TODO: Convert `&mut *mut_foo` into `mut_foo` if the source is movable and not used afterwards - -#if DUMP_BEFORE_ALL || DUMP_BEFORE_PSA - if( debug_enabled() ) MIR_Dump_Fcn(::std::cout, fcn); -#endif - // >> Propagate/remove dead assignments - while( MIR_Optimise_PropagateSingleAssignments(state, fcn) ) - change_happened = true; -#if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif - - // >> Move common statements (assignments) across gotos. - change_happened |= MIR_Optimise_CommonStatements(state, fcn); - - // >> Combine Duplicate Blocks - change_happened |= MIR_Optimise_UnifyBlocks(state, fcn); - // >> Remove assignments of unsed drop flags - change_happened |= MIR_Optimise_DeadDropFlags(state, fcn); - // >> Remove assignments that are never read - change_happened |= MIR_Optimise_DeadAssignments(state, fcn); - // >> Remove no-op assignments - change_happened |= MIR_Optimise_NoopRemoval(state, fcn); - - #if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); - #endif - - // >> Inline short functions - if( !change_happened ) - { - bool inline_happened = MIR_Optimise_Inlining(state, fcn, false); - if( inline_happened ) - { - // Apply cleanup again (as monomorpisation in inlining may have exposed a vtable call) - MIR_Cleanup(resolve, path, fcn, args, ret_type); - //MIR_Dump_Fcn(::std::cout, fcn); - change_happened = true; - } - #if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); - #endif - } - - if( change_happened ) - { - #if DUMP_AFTER_PASS - if( debug_enabled() ) { - MIR_Dump_Fcn(::std::cout, fcn); - } - #endif - #if CHECK_AFTER_PASS && !CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); - #endif - } - - MIR_Optimise_GarbageCollect_Partial(state, fcn); - pass_num += 1; - } while( change_happened ); - - // Run UnifyTemporaries last, then unify blocks, then run some - // optimisations that might be affected - if(MIR_Optimise_UnifyTemporaries(state, fcn)) - { -#if CHECK_AFTER_ALL - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif - MIR_Optimise_UnifyBlocks(state, fcn); - //MIR_Optimise_ConstPropagte(state, fcn); - MIR_Optimise_NoopRemoval(state, fcn); - } - - - #if DUMP_AFTER_DONE - if( debug_enabled() ) { - MIR_Dump_Fcn(::std::cout, fcn); - } - #endif - #if CHECK_AFTER_DONE - // DEFENCE: Run validation _before_ GC (so validation errors refer to the pre-gc numbers) - MIR_Validate(resolve, path, fcn, args, ret_type); - #endif - // GC pass on blocks and variables - // - Find unused blocks, then delete and rewrite all references. - MIR_Optimise_GarbageCollect(state, fcn); - - //MIR_Validate_Full(resolve, path, fcn, args, ret_type); - - MIR_SortBlocks(resolve, path, fcn); -#if CHECK_AFTER_DONE > 1 - MIR_Validate(resolve, path, fcn, args, ret_type); -#endif -} // -------------------------------------------------------------------- // Performs basic simplications on the call graph (merging/removing blocks) @@ -1540,7 +1555,7 @@ bool MIR_Optimise_CommonStatements(::MIR::TypeResolve& state, ::MIR::Function& f bool skip = false; ::std::vector<size_t> sources; // Find source blocks - for(size_t bb2_idx = 0; bb2_idx < fcn.blocks.size(); bb2_idx ++) + for(size_t bb2_idx = 0; bb2_idx < fcn.blocks.size() && !skip; bb2_idx ++) { const auto& blk = fcn.blocks[bb2_idx]; // TODO: Handle non-Goto branches? (e.g. calls) @@ -1566,6 +1581,7 @@ bool MIR_Optimise_CommonStatements(::MIR::TypeResolve& state, ::MIR::Function& f else { visit_terminator_target(blk.terminator, [&](const auto& dst_idx) { + // If this terminator points to the current BB, don't attempt to merge if( dst_idx == bb_idx ) { DEBUG(state << " BB" << bb2_idx << " doesn't end Goto - instead " << blk.terminator); skip = true; @@ -1576,6 +1592,8 @@ bool MIR_Optimise_CommonStatements(::MIR::TypeResolve& state, ::MIR::Function& f if( !skip && sources.size() > 1 ) { + // TODO: Should this search for any common statements? + // Found a common assignment, add to the start and remove from sources. auto stmt = ::std::move(fcn.blocks[sources.front()].statements.back()); MIR_DEBUG(state, "Move common final statements from " << sources << " to " << bb_idx << " - " << stmt); diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 0171f19f..5df20334 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -559,12 +559,24 @@ namespace { switch( m_compiler ) { case Compiler::Gcc: - if( getenv("CC") ) { - args.push_back( getenv("CC") ); - } - else { - //args.push_back( Target_GetCurSpec().m_c_compiler + "-gcc" ); - args.push_back( "gcc" ); + // Pick the compiler + // - from `CC-${TRIPLE}` environment variable + // - from the $CC environment variable + // - `gcc-${TRIPLE}` (if available) + // - `gcc` as fallback + { + ::std::string varname = "CC-" + Target_GetCurSpec().m_c_compiler; + if( getenv(varname.c_str()) ) { + args.push_back( getenv(varname.c_str()) ); + } + else if( getenv("CC") ) { + args.push_back( getenv("CC") ); + } + else { + // TODO: Determine if the compiler can't be found, and fall back to `gcc` if that's the case + args.push_back( Target_GetCurSpec().m_c_compiler + "-gcc" ); + //args.push_back( "gcc" ); + } } args.push_back("-ffunction-sections"); args.push_back("-pthread"); diff --git a/src/trans/target.cpp b/src/trans/target.cpp index 2bf655a9..81dc8c0c 100644 --- a/src/trans/target.cpp +++ b/src/trans/target.cpp @@ -54,7 +54,18 @@ namespace auto check_path_length = [&](const TomlKeyValue& kv, unsigned len) { if( kv.path.size() != len ) { - // TODO: Error. + if( kv.path.size() > len ) { + ::std::cerr << "ERROR: Unexpected sub-node to " << kv.path << " in " << filename << ::std::endl; + } + else { + ::std::cerr << "ERROR: Expected sub-nodes in " << kv.path << " in " << filename << ::std::endl; + } + exit(1); + } + }; + auto check_path_length_min = [&](const TomlKeyValue& kv, unsigned len) { + if( kv.path.size() < len ) { + ::std::cerr << "ERROR: Expected sub-nodes in " << kv.path << " in " << filename << ::std::endl; } }; @@ -62,6 +73,7 @@ namespace { if( key_val.path[0] == "target" ) { + check_path_length_min(key_val, 2); if( key_val.path[1] == "family" ) { check_path_length(key_val, 2); @@ -77,11 +89,6 @@ namespace check_path_length(key_val, 2); rv.m_env_name = key_val.value.as_string(); } - else if( key_val.path[1] == "env-name" ) - { - check_path_length(key_val, 2); - rv.m_env_name = key_val.value.as_string(); - } else if( key_val.path[1] == "arch" ) { check_path_length(key_val, 2); @@ -103,22 +110,23 @@ namespace } else { - // TODO: Error. + // Error. + ::std::cerr << "ERROR: Unknown architecture name '" << key_val.value.as_string() << "' in " << filename << ::std::endl; + exit(1); } } else { - // TODO: Error/warning + // Warning + ::std::cerr << "Warning: Unknown configuration item " << key_val.path[0] << "." << key_val.path[1] << " in " << filename << ::std::endl; } } else if( key_val.path[0] == "backend" ) { + check_path_length_min(key_val, 2); if( key_val.path[1] == "c" ) { - if( key_val.path.size() <= 2 ) { - // TODO: Error. - continue ; - } + check_path_length_min(key_val, 3); if( key_val.path[2] == "variant" ) { @@ -133,7 +141,8 @@ namespace } else { - // TODO: Error. + ::std::cerr << "ERROR: Unknown C variant name '" << key_val.value.as_string() << "' in " << filename << ::std::endl; + exit(1); } } else if( key_val.path[2] == "target" ) @@ -143,71 +152,90 @@ namespace } else { - // TODO: Warning/error + ::std::cerr << "WARNING: Unknown field backend.c." << key_val.path[2] << " in " << filename << ::std::endl; } } // Does MMIR need configuration? else { - // TODO: Error/warning + ::std::cerr << "WARNING: Unknown configuration item backend." << key_val.path[1] << " in " << filename << ::std::endl; } } else if( key_val.path[0] == "arch" ) { + check_path_length_min(key_val, 2); if( key_val.path[1] == "name" ) { + check_path_length(key_val, 2); + if( rv.m_arch.m_name != "" ) { + ::std::cerr << "ERROR: Architecture already specified to be '" << rv.m_arch.m_name << "'" << ::std::endl; + exit(1); + } rv.m_arch.m_name = key_val.value.as_string(); } else if( key_val.path[1] == "pointer-bits" ) { + check_path_length(key_val, 2); rv.m_arch.m_pointer_bits = key_val.value.as_int(); } else if( key_val.path[1] == "is-big-endian" ) { + check_path_length(key_val, 2); rv.m_arch.m_big_endian = key_val.value.as_bool(); } else if( key_val.path[1] == "has-atomic-u8" ) { + check_path_length(key_val, 2); rv.m_arch.m_atomics.u8 = key_val.value.as_bool(); } else if( key_val.path[1] == "has-atomic-u16" ) { + check_path_length(key_val, 2); rv.m_arch.m_atomics.u16 = key_val.value.as_bool(); } else if( key_val.path[1] == "has-atomic-u32" ) { + check_path_length(key_val, 2); rv.m_arch.m_atomics.u32 = key_val.value.as_bool(); } else if( key_val.path[1] == "has-atomic-u64" ) { + check_path_length(key_val, 2); rv.m_arch.m_atomics.u64 = key_val.value.as_bool(); } else if( key_val.path[1] == "has-atomic-ptr" ) { + check_path_length(key_val, 2); rv.m_arch.m_atomics.ptr = key_val.value.as_bool(); } else { - // TODO: warning/error + ::std::cerr << "WARNING: Unknown field arch." << key_val.path[1] << " in " << filename << ::std::endl; } } else { + ::std::cerr << "WARNING: Unknown configuration item " << key_val.path[0] << " in " << filename << ::std::endl; } } catch(const TomlValue::TypeError& e) { - // TODO: error + ::std::cerr << "ERROR: Invalid type for " << key_val.path << " - " << e << ::std::endl; + exit(1); } } // TODO: Ensure that everything is set + if( rv.m_arch.m_name == "" ) { + ::std::cerr << "ERROR: Architecture not specified in " << filename << ::std::endl; + exit(1); + } return rv; } TargetSpec init_from_spec_name(const ::std::string& target_name) { - // TODO: If there's a '/' or a '\' in the filename, open it as a path, otherwise it has to be a triple. + // If there's a '/' or a '\' in the filename, open it as a path, otherwise assume it's a triple. if( target_name.find('/') != ::std::string::npos || target_name.find('\\') != ::std::string::npos ) { return load_spec_from_file(target_name); @@ -355,6 +383,9 @@ void Target_SetCfg(const ::std::string& target_name) Cfg_SetValue("target_arch", g_target.m_arch.m_name); Cfg_SetValueCb("target_has_atomic", [&](const ::std::string& s) { if(s == "8") return g_target.m_arch.m_atomics.u8; // Has an atomic byte + if(s == "16") return g_target.m_arch.m_atomics.u16; + if(s == "32") return g_target.m_arch.m_atomics.u32; + if(s == "64") return g_target.m_arch.m_atomics.u64; if(s == "ptr") return g_target.m_arch.m_atomics.ptr; // Has an atomic pointer-sized value return false; }); diff --git a/tools/common/toml.h b/tools/common/toml.h index e57c28ae..a2c03bf1 100644 --- a/tools/common/toml.h +++ b/tools/common/toml.h @@ -42,7 +42,18 @@ struct TomlValue String, Integer, List, + }; + friend ::std::ostream& operator<<(::std::ostream& os, const Type& e) { + switch(e) + { + case Type::Boolean: os << "boolean"; break; + case Type::String: os << "string"; break; + case Type::Integer: os << "integer"; break; + case Type::List: os << "list"; break; + } + return os; + } struct TypeError: public ::std::exception { @@ -58,6 +69,10 @@ struct TomlValue const char* what() const noexcept override { return "toml type error"; } + friend ::std::ostream& operator<<(::std::ostream& os, const TypeError& e) { + os << "expected " << e.exp << ", got " << e.have; + return os; + } }; Type m_type; |