diff --git a/2 b/2 new file mode 100644 index 0000000000..b0702f970a --- /dev/null +++ b/2 @@ -0,0 +1,804 @@ +#[macro_use] +extern crate pretty_assertions; +#[macro_use] +extern crate indoc; + +extern crate bumpalo; +extern crate inkwell; +extern crate libc; +extern crate roc_gen; + +#[macro_use] +mod helpers; + +#[cfg(test)] +mod gen_tags { + use crate::helpers::{can_expr, infer_expr, uniq_expr, CanExprOut}; + use bumpalo::Bump; + use inkwell::context::Context; + use inkwell::execution_engine::JitFunction; + use inkwell::passes::PassManager; + use inkwell::types::BasicType; + use inkwell::OptimizationLevel; + use roc_collections::all::ImMap; + use roc_gen::llvm::build::{build_proc, build_proc_header}; + use roc_gen::llvm::convert::basic_type_from_layout; + use roc_mono::expr::{Expr, Procs}; + use roc_mono::layout::Layout; + use roc_types::subs::Subs; + + use inkwell::targets::{ + CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple, + }; + use std::path::Path; + + #[test] + fn applied_tag_nothing() { + assert_evals_to!( + indoc!( + r#" + Maybe a : [ Just a, Nothing ] + + x : Maybe Int + x = Nothing + + 0x1 + "# + ), + 1, + i64 + ); + } + + #[test] + fn applied_tag_just() { + assert_evals_to!( + indoc!( + r#" + Maybe a : [ Just a, Nothing ] + + y : Maybe Int + y = Just 0x4 + + 0x1 + "# + ), + 1, + i64 + ); + } + + #[test] + fn applied_tag_just_unit() { + assert_evals_to!( + indoc!( + r#" + Fruit : [ Orange, Apple, Banana ] + Maybe a : [ Just a, Nothing ] + + orange : Fruit + orange = Orange + + y : Maybe Fruit + y = Just orange + + 0x1 + "# + ), + 1, + i64 + ); + } + + // #[test] + // fn raw_result() { + // assert_evals_to!( + // indoc!( + // r#" + // x : Result Int Int + // x = Err 41 + + // x + // "# + // ), + // 0, + // i8 + // ); + // } + + #[test] + fn true_is_true() { + assert_evals_to!( + indoc!( + r#" + bool : [True, False] + bool = True + + bool + "# + ), + true, + bool + ); + } + + #[test] + fn false_is_false() { + assert_evals_to!( + indoc!( + r#" + bool : [True, False] + bool = False + + bool + "# + ), + false, + bool + ); + } + + #[test] + fn basic_enum() { + assert_evals_to!( + indoc!( + r#" + Fruit : [ Apple, Orange, Banana ] + + apple : Fruit + apple = Apple + + orange : Fruit + orange = Orange + + apple == orange + "# + ), + false, + bool + ); + } + + // #[test] + // fn linked_list_empty() { + // assert_evals_to!( + // indoc!( + // r#" + // LinkedList a : [ Cons a (LinkedList a), Nil ] + // + // empty : LinkedList Int + // empty = Nil + // + // 1 + // "# + // ), + // 1, + // i64 + // ); + // } + // + // #[test] + // fn linked_list_singleton() { + // assert_evals_to!( + // indoc!( + // r#" + // LinkedList a : [ Cons a (LinkedList a), Nil ] + // + // singleton : LinkedList Int + // singleton = Cons 0x1 Nil + // + // 1 + // "# + // ), + // 1, + // i64 + // ); + // } + // + // #[test] + // fn linked_list_is_empty() { + // assert_evals_to!( + // indoc!( + // r#" + // LinkedList a : [ Cons a (LinkedList a), Nil ] + // + // isEmpty : LinkedList a -> Bool + // isEmpty = \list -> + // when list is + // Nil -> True + // Cons _ _ -> False + // + // isEmpty (Cons 4 Nil) + // "# + // ), + // false, + // bool + // ); + // } + + #[test] + fn even_odd() { + assert_evals_to!( + indoc!( + r#" + even = \n -> + when n is + 0 -> True + 1 -> False + _ -> odd (n - 1) + + odd = \n -> + when n is + 0 -> False + 1 -> True + _ -> even (n - 1) + + odd 5 && even 42 + "# + ), + true, + bool + ); + } + + #[test] + fn gen_literal_true() { + assert_evals_to!( + indoc!( + r#" + if True then -1 else 1 + "# + ), + -1, + i64 + ); + } + + #[test] + fn gen_if_float() { + assert_evals_to!( + indoc!( + r#" + if True then -1.0 else 1.0 + "# + ), + -1.0, + f64 + ); + } + #[test] + fn when_on_nothing() { + assert_evals_to!( + indoc!( + r#" + x : [ Nothing, Just Int ] + x = Nothing + + when x is + Nothing -> 0x2 + Just _ -> 0x1 + "# + ), + 2, + i64 + ); + } + + #[test] + fn when_on_just() { + assert_evals_to!( + indoc!( + r#" + x : [ Nothing, Just Int ] + x = Just 41 + + when x is + Just v -> v + 0x1 + Nothing -> 0x1 + "# + ), + 42, + i64 + ); + } + + #[test] + fn when_on_result() { + assert_evals_to!( + indoc!( + r#" + x : Result Int Int + x = Err 41 + + when x is + Err v -> v + 1 + Ok _ -> 1 + "# + ), + 42, + i64 + ); + } + + #[test] + fn when_on_these() { + assert_evals_to!( + indoc!( + r#" + These a b : [ This a, That b, These a b ] + + x : These Int Int + x = These 0x3 0x2 + + when x is + These a b -> a + b + That v -> 8 + This v -> v + "# + ), + 5, + i64 + ); + } + + #[test] + fn match_on_two_values() { + // this will produce a Chain internally + assert_evals_to!( + indoc!( + r#" + when Pair 2 3 is + Pair 4 3 -> 9 + Pair a b -> a + b + "# + ), + 5, + i64 + ); + } + + #[test] + fn pair_with_guard_pattern() { + assert_evals_to!( + indoc!( + r#" + when Pair 2 3 is + Pair 4 _ -> 1 + Pair 3 _ -> 2 + Pair a b -> a + b + "# + ), + 5, + i64 + ); + } + + #[test] + fn result_with_guard_pattern() { + // This test revealed an issue with hashing Test values + assert_evals_to!( + indoc!( + r#" + x : Result Int Int + x = Ok 2 + + when x is + Ok 3 -> 1 + Ok _ -> 2 + Err _ -> 3 + "# + ), + 2, + i64 + ); + } + + #[test] + fn maybe_is_just() { + assert_evals_to!( + indoc!( + r#" + Maybe a : [ Just a, Nothing ] + + isJust : Maybe a -> Bool + isJust = \list -> + when list is + Nothing -> False + Just _ -> True + + isJust (Just 42) + "# + ), + true, + bool + ); + } + + #[test] + fn nested_pattern_match() { + assert_evals_to!( + indoc!( + r#" + Maybe a : [ Nothing, Just a ] + + x : Maybe (Maybe Int) + x = Just (Just 41) + + when x is + Just (Just v) -> v + 0x1 + _ -> 0x1 + "# + ), + 42, + i64 + ); + } + #[test] + fn if_guard_pattern_false() { + assert_evals_to!( + indoc!( + r#" + when 2 is + 2 if False -> 0 + _ -> 42 + "# + ), + 42, + i64 + ); + } + + #[test] + fn if_guard_pattern_true() { + assert_evals_to!( + indoc!( + r#" + when 2 is + 2 if True -> 42 + _ -> 0 + "# + ), + 42, + i64 + ); + } + + #[test] + fn if_guard_exhaustiveness() { + assert_evals_to!( + indoc!( + r#" + when 2 is + _ if False -> 0 + _ -> 42 + "# + ), + 42, + i64 + ); + } + + #[test] + fn when_on_enum() { + assert_evals_to!( + indoc!( + r#" + Fruit : [ Apple, Orange, Banana ] + + apple : Fruit + apple = Apple + + when apple is + Apple -> 1 + Banana -> 2 + Orange -> 3 + "# + ), + 1, + i64 + ); + } + + #[test] + fn pattern_matching_unit() { + assert_evals_to!( + indoc!( + r#" + Unit : [ Unit ] + + f : Unit -> Int + f = \Unit -> 42 + + f Unit + "# + ), + 42, + i64 + ); + + assert_evals_to!( + indoc!( + r#" + Unit : [ Unit ] + + x : Unit + x = Unit + + when x is + Unit -> 42 + "# + ), + 42, + i64 + ); + + assert_evals_to!( + indoc!( + r#" + f : {} -> Int + f = \{} -> 42 + + f {} + "# + ), + 42, + i64 + ); + + assert_evals_to!( + indoc!( + r#" + when {} is + {} -> 42 + "# + ), + 42, + i64 + ); + } + + #[test] + fn one_element_tag() { + assert_evals_to!( + indoc!( + r#" + x : [ Pair Int ] + x = Pair 2 + + 0x3 + "# + ), + 3, + i64 + ); + } + + #[test] + fn nested_tag_union() { + assert_evals_to!( + indoc!( + r#" + Maybe a : [ Nothing, Just a ] + + x : Maybe (Maybe a) + x = Just (Just 41) + + 5 + "# + ), + 5, + i64 + ); + } + #[test] + fn unit_type() { + assert_evals_to!( + indoc!( + r#" + Unit : [ Unit ] + + v : Unit + v = Unit + + 1 + "# + ), + 1, + i64 + ); + } + + #[test] + fn nested_record_load() { + assert_evals_to!( + indoc!( + r#" + Maybe a : [ Nothing, Just a ] + + x = { a : { b : 0x5 } } + + y = x.a + + y.b + "# + ), + 5, + i64 + ); + } + + #[test] + fn test_emit() { + let src = "42"; + + // Build the expr + let arena = Bump::new(); + let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr(src); + + let mut unify_problems = Vec::new(); + let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); + + let context = Context::create(); + let module = context.create_module("app"); + let builder = context.create_builder(); + let fpm = PassManager::create(&module); + + roc_gen::llvm::build::add_passes(&fpm); + + fpm.initialize(); + + // Compute main_fn_type before moving subs to Env + let layout = Layout::from_content(&arena, content, &subs, crate::helpers::eval::POINTER_SIZE) + .unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); + + let execution_engine = + module + .create_jit_execution_engine(OptimizationLevel::None) + .expect("Error creating JIT execution engine for test"); + + let ptr_bytes = execution_engine.get_target_data().get_pointer_byte_size(None); + let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) + .fn_type(&[], false); + let main_fn_name = "$Test.main"; + + // Compile and add all the Procs before adding main + let mut env = roc_gen::llvm::build::Env { + arena: &arena, + builder: &builder, + context: &context, + interns, + module: arena.alloc(module), + ptr_bytes + }; + let mut procs = Procs::default(); + let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); + + // Populate Procs and get the low-level Expr from the canonical Expr + let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, $crate::helpers::eval::POINTER_SIZE); + + // Put this module's ident_ids back in the interns, so we can use them in Env. + env.interns.all_ident_ids.insert(home, ident_ids); + + let mut headers = Vec::with_capacity(procs.len()); + + // Add all the Proc headers to the module. + // We have to do this in a separate pass first, + // because their bodies may reference each other. + for (symbol, opt_proc) in procs.as_map().into_iter() { + if let Some(proc) = opt_proc { + let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc); + + headers.push((proc, fn_val, arg_basic_types)); + } + } + + // Build each proc using its header info. + for (proc, fn_val, arg_basic_types) in headers { + // NOTE: This is here to be uncommented in case verification fails. + // (This approach means we don't have to defensively clone name here.) + // + // println!("\n\nBuilding and then verifying function {}\n\n", name); + build_proc(&env, proc, &procs, fn_val, arg_basic_types); + + if fn_val.verify(true) { + fpm.run_on(&fn_val); + } else { + // NOTE: If this fails, uncomment the above println to debug. + panic!("Non-main function failed LLVM verification. Uncomment the above println to debug!"); + } + } + + // Add main to the module. + let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); + + main_fn.set_call_conventions($crate::helpers::eval::MAIN_CALLING_CONVENTION); + + // Add main's body + let basic_block = context.append_basic_block(main_fn, "entry"); + + builder.position_at_end(basic_block); + + let ret = roc_gen::llvm::build::build_expr( + &env, + &ImMap::default(), + main_fn, + &main_body, + &mut Procs::default(), + ); + + builder.build_return(Some(&ret)); + + // Uncomment this to see the module's un-optimized LLVM instruction output: + // env.module.print_to_stderr(); + + if main_fn.verify(true) { + fpm.run_on(&main_fn); + } else { + panic!("Function {} failed LLVM verification.", main_fn_name); + } + + // Verify the module + if let Err(errors) = env.module.verify() { + panic!("Errors defining module: {:?}", errors); + } + + // Uncomment this to see the module's optimized LLVM instruction output: + // env.module.print_to_stderr(); + + // Emit + Target::initialize_x86(&InitializationConfig::default()); + + let opt = OptimizationLevel::Default; + let reloc = RelocMode::Default; + let model = CodeModel::Default; + let target = Target::from_name("x86-64").unwrap(); + let target_machine = target.create_target_machine( + &TargetTriple::create("x86_64-pc-linux-gnu"), + "x86-64", + "+avx2", + opt, + reloc, + model + ) + .unwrap(); + + let buffer = target_machine.write_to_memory_buffer(&module, FileType::Assembly).unwrap(); + + let buffer_str = std::str::from_utf8(buffer).unwrap(); + + } + + #[test] + fn test_emit_old() { + Target::initialize_x86(&InitializationConfig::default()); + + let opt = OptimizationLevel::Default; + let reloc = RelocMode::Default; + let model = CodeModel::Default; + let path = Path::new("./test.asm"); + let target = Target::from_name("x86-64").unwrap(); + let target_machine = target + .create_target_machine( + &TargetTriple::create("x86_64-pc-linux-gnu"), + "x86-64", + "+avx2", + opt, + reloc, + model, + ) + .unwrap(); + + let context = Context::create(); + let module = context.create_module("my_module"); + let void_type = context.void_type(); + let fn_type = void_type.fn_type(&[], false); + + module.add_function("my_fn", fn_type, None); + + target_machine + .write_to_file(&module, FileType::Assembly, &path) + .expect("writing to file succeeded"); + + panic!("TODO remove this test"); + } +} diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md index a13d3b5ecf..172a845752 100644 --- a/BUILDING_FROM_SOURCE.md +++ b/BUILDING_FROM_SOURCE.md @@ -22,3 +22,21 @@ Caused by: ``` This seems to be caused by cargo being out of date (even if it's freshly installed), and can be fixed with `cargo update`. + +## Use LLD for the linker + +Using [`lld` for Rust's linker](https://github.com/rust-lang/rust/issues/39915#issuecomment-538049306) +makes build times a lot faster, and I highly recommend it. + +Create `~/.config/cargo` and add this to it: + +``` +[build] +rustflags = ["-C", "link-arg=-fuse-ld=lld"] +``` + +Then install `lld` version 9 (e.g. with `$ sudo apt-get install lld-9`) +and add make sure there's a `ld.lld` executable on your `PATH` which +is symlinked to `lld-9`. + +That's it! Enjoy the faster builds. diff --git a/Cargo.lock b/Cargo.lock index d8c1dca795..141806b2a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,6 +526,32 @@ version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +[[package]] +name = "roc-cli" +version = "0.1.0" +dependencies = [ + "bumpalo", + "im", + "im-rc", + "inkwell", + "inlinable_string", + "roc_builtins", + "roc_can", + "roc_collections", + "roc_constrain", + "roc_gen", + "roc_module", + "roc_mono", + "roc_parse", + "roc_problem", + "roc_region", + "roc_solve", + "roc_types", + "roc_unify", + "roc_uniq", + "target-lexicon", +] + [[package]] name = "roc_builtins" version = "0.1.0" @@ -642,6 +668,7 @@ dependencies = [ "roc_types", "roc_unify", "roc_uniq", + "target-lexicon", "tokio", ] @@ -904,6 +931,12 @@ dependencies = [ "unicode-xid 0.2.0", ] +[[package]] +name = "target-lexicon" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" + [[package]] name = "thread_local" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 9aa94be014..9a713fbff2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,7 @@ members = [ "compiler/load", "compiler/gen", "vendor/ena", - "vendor/pathfinding" + "vendor/pathfinding", + "cli" ] + diff --git a/a.out b/a.out new file mode 100755 index 0000000000..42cca021e2 Binary files /dev/null and b/a.out differ diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 30c7aadbba..5b471cfb90 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -8,6 +8,50 @@ keywords = ["roc", "gui"] edition = "2018" description = "A CLI for Roc" license = "Apache-2.0" +default-run = "roc" + +[[bin]] +name = "roc" +path = "src/main.rs" +test = false +bench = false + [dependencies] -roc = { path = "../", version = "0.1.0" } +roc_collections = { path = "../compiler/collections" } +roc_can = { path = "../compiler/can" } +roc_parse = { path = "../compiler/parse" } +roc_region = { path = "../compiler/region" } +roc_module = { path = "../compiler/module" } +roc_problem = { path = "../compiler/problem" } +roc_types = { path = "../compiler/types" } +roc_builtins = { path = "../compiler/builtins" } +roc_constrain = { path = "../compiler/constrain" } +roc_uniq = { path = "../compiler/uniq" } +roc_unify = { path = "../compiler/unify" } +roc_solve = { path = "../compiler/solve" } +roc_mono = { path = "../compiler/mono" } +roc_gen = { path = "../compiler/gen", version = "0.1.0" } +im = "14" # im and im-rc should always have the same version! +im-rc = "14" # im and im-rc should always have the same version! +bumpalo = { version = "3.2", features = ["collections"] } +inlinable_string = "0.1.0" +# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. +# +# The reason for this fork is that the way Inkwell is designed, you have to use +# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that +# breaking changes get pushed directly to that branch, which breaks our build +# without warning. +# +# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch), +# but although that worked locally, it did not work on GitHub Actions. (After a few +# hours of investigation, gave up trying to figure out why.) So this is the workaround: +# having an immutable tag on the rtfeldman/inkwell fork which points to +# a particular "release" of Inkwell. +# +# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest +# commit of TheDan64/inkwell, push a new tag which points to the latest commit, +# change the tag value in this Cargo.toml to point to that tag, and `cargo update`. +# This way, GitHub Actions works and nobody's builds get broken. +inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm8-0.release2" } +target-lexicon = "0.10" diff --git a/cli/src/helpers.rs b/cli/src/helpers.rs new file mode 100644 index 0000000000..9ed8b4dc12 --- /dev/null +++ b/cli/src/helpers.rs @@ -0,0 +1,396 @@ +use bumpalo::Bump; +use roc_builtins::unique::uniq_stdlib; +use roc_can::constraint::Constraint; +use roc_can::env::Env; +use roc_can::expected::Expected; +use roc_can::expr::{canonicalize_expr, Expr, Output}; +use roc_can::operator; +use roc_can::scope::Scope; +use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet}; +use roc_constrain::expr::constrain_expr; +use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import}; +use roc_module::ident::Ident; +use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol}; +use roc_parse::ast::{self, Attempting}; +use roc_parse::blankspace::space0_before; +use roc_parse::parser::{loc, Fail, Parser, State}; +use roc_problem::can::Problem; +use roc_region::all::{Located, Region}; +use roc_solve::solve; +use roc_types::subs::{Content, Subs, VarStore, Variable}; +use roc_types::types::Type; +use std::hash::Hash; + +pub fn test_home() -> ModuleId { + ModuleIds::default().get_or_insert(&"Test".into()) +} + +pub fn infer_expr( + subs: Subs, + problems: &mut Vec, + constraint: &Constraint, + expr_var: Variable, +) -> (Content, Subs) { + let env = solve::Env { + aliases: MutMap::default(), + vars_by_symbol: SendMap::default(), + }; + let (solved, _) = solve::run(&env, problems, subs, constraint); + + let content = solved.inner().get_without_compacting(expr_var).content; + + (content, solved.into_inner()) +} + +pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, Fail> { + parse_loc_with(arena, input).map(|loc_expr| loc_expr.value) +} + +pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result>, Fail> { + let state = State::new(&input, Attempting::Module); + let parser = space0_before(loc(roc_parse::expr::expr(0)), 0); + let answer = parser.parse(&arena, state); + + answer + .map(|(loc_expr, _)| loc_expr) + .map_err(|(fail, _)| fail) +} + +pub fn can_expr(expr_str: &str) -> CanExprOut { + can_expr_with(&Bump::new(), test_home(), expr_str) +} + +pub fn uniq_expr( + expr_str: &str, +) -> ( + Located, + Output, + Vec, + Subs, + Variable, + Constraint, + ModuleId, + Interns, +) { + let declared_idents: &ImMap = &ImMap::default(); + + uniq_expr_with(&Bump::new(), expr_str, declared_idents) +} + +pub fn uniq_expr_with( + arena: &Bump, + expr_str: &str, + declared_idents: &ImMap, +) -> ( + Located, + Output, + Vec, + Subs, + Variable, + Constraint, + ModuleId, + Interns, +) { + let home = test_home(); + let CanExprOut { + loc_expr, + output, + problems, + var_store: old_var_store, + var, + interns, + .. + } = can_expr_with(arena, home, expr_str); + + // double check + let var_store = VarStore::new(old_var_store.fresh()); + + let expected2 = Expected::NoExpectation(Type::Variable(var)); + let constraint = roc_constrain::uniq::constrain_declaration( + home, + &var_store, + Region::zero(), + &loc_expr, + declared_idents, + expected2, + ); + + let stdlib = uniq_stdlib(); + + let types = stdlib.types; + let imports: Vec<_> = types + .iter() + .map(|(symbol, (solved_type, region))| Import { + loc_symbol: Located::at(*region, *symbol), + solved_type, + }) + .collect(); + + // load builtin values + + // TODO what to do with those rigids? + let (_introduced_rigids, constraint) = + constrain_imported_values(imports, constraint, &var_store); + + // load builtin types + let mut constraint = load_builtin_aliases(&stdlib.aliases, constraint, &var_store); + + constraint.instantiate_aliases(&var_store); + + let subs2 = Subs::new(var_store.into()); + + ( + loc_expr, output, problems, subs2, var, constraint, home, interns, + ) +} + +pub struct CanExprOut { + pub loc_expr: Located, + pub output: Output, + pub problems: Vec, + pub home: ModuleId, + pub interns: Interns, + pub var_store: VarStore, + pub var: Variable, + pub constraint: Constraint, +} + +pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut { + let loc_expr = parse_loc_with(&arena, expr_str).unwrap_or_else(|e| { + panic!( + "can_expr_with() got a parse error when attempting to canonicalize:\n\n{:?} {:?}", + expr_str, e + ) + }); + + let var_store = VarStore::default(); + let var = var_store.fresh(); + let expected = Expected::NoExpectation(Type::Variable(var)); + let module_ids = ModuleIds::default(); + + // Desugar operators (convert them to Apply calls, taking into account + // operator precedence and associativity rules), before doing other canonicalization. + // + // If we did this *during* canonicalization, then each time we + // visited a BinOp node we'd recursively try to apply this to each of its nested + // operators, and then again on *their* nested operators, ultimately applying the + // rules multiple times unnecessarily. + let loc_expr = operator::desugar_expr(arena, &loc_expr); + + let mut scope = Scope::new(home); + let dep_idents = IdentIds::exposed_builtins(0); + let mut env = Env::new(home, dep_idents, &module_ids, IdentIds::default()); + let (loc_expr, output) = canonicalize_expr( + &mut env, + &var_store, + &mut scope, + Region::zero(), + &loc_expr.value, + ); + + let constraint = constrain_expr( + &roc_constrain::expr::Env { + rigids: ImMap::default(), + home, + }, + loc_expr.region, + &loc_expr.value, + expected, + ); + + let types = roc_builtins::std::types(); + + let imports: Vec<_> = types + .iter() + .map(|(symbol, (solved_type, region))| Import { + loc_symbol: Located::at(*region, *symbol), + solved_type, + }) + .collect(); + + //load builtin values + let (_introduced_rigids, constraint) = + constrain_imported_values(imports, constraint, &var_store); + + // TODO determine what to do with those rigids + // for var in introduced_rigids { + // output.ftv.insert(var, format!("internal_{:?}", var).into()); + // } + + //load builtin types + let mut constraint = + load_builtin_aliases(&roc_builtins::std::aliases(), constraint, &var_store); + + constraint.instantiate_aliases(&var_store); + + let mut all_ident_ids = MutMap::default(); + + // When pretty printing types, we may need the exposed builtins, + // so include them in the Interns we'll ultimately return. + for (module_id, ident_ids) in IdentIds::exposed_builtins(0) { + all_ident_ids.insert(module_id, ident_ids); + } + + all_ident_ids.insert(home, env.ident_ids); + + let interns = Interns { + module_ids: env.module_ids.clone(), + all_ident_ids, + }; + + CanExprOut { + loc_expr, + output, + problems: env.problems, + home: env.home, + var_store, + interns, + var, + constraint, + } +} + +pub fn mut_map_from_pairs(pairs: I) -> MutMap +where + I: IntoIterator, + K: Hash + Eq, +{ + let mut answer = MutMap::default(); + + for (key, value) in pairs { + answer.insert(key, value); + } + + answer +} + +pub fn im_map_from_pairs(pairs: I) -> ImMap +where + I: IntoIterator, + K: Hash + Eq + Clone, + V: Clone, +{ + let mut answer = ImMap::default(); + + for (key, value) in pairs { + answer.insert(key, value); + } + + answer +} + +pub fn send_set_from(elems: I) -> SendSet +where + I: IntoIterator, + V: Hash + Eq + Clone, +{ + let mut answer = SendSet::default(); + + for elem in elems { + answer.insert(elem); + } + + answer +} + +// Check constraints +// +// Keep track of the used (in types or expectations) variables, and the declared variables (in +// flex_vars or rigid_vars fields of LetConstraint. These roc_collections should match: no duplicates +// and no variables that are used but not declared are allowed. +// +// There is one exception: the initial variable (that stores the type of the whole expression) is +// never declared, but is used. +pub fn assert_correct_variable_usage(constraint: &Constraint) { + // variables declared in constraint (flex_vars or rigid_vars) + // and variables actually used in constraints + let (declared, used) = variable_usage(constraint); + + let used: ImSet = used.into(); + let mut decl: ImSet = declared.rigid_vars.clone().into(); + + for var in declared.flex_vars.clone() { + decl.insert(var); + } + + let diff = used.clone().relative_complement(decl); + + // NOTE: this checks whether we're using variables that are not declared. For recursive type + // definitions, their rigid types are declared twice, which is correct! + if !diff.is_empty() { + println!("VARIABLE USAGE PROBLEM"); + + println!("used: {:?}", &used); + println!("rigids: {:?}", &declared.rigid_vars); + println!("flexs: {:?}", &declared.flex_vars); + + println!("difference: {:?}", &diff); + + panic!("variable usage problem (see stdout for details)"); + } +} + +#[derive(Default)] +pub struct SeenVariables { + pub rigid_vars: Vec, + pub flex_vars: Vec, +} + +pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec) { + let mut declared = SeenVariables::default(); + let mut used = ImSet::default(); + variable_usage_help(con, &mut declared, &mut used); + + used.remove(unsafe { &Variable::unsafe_test_debug_variable(1) }); + + let mut used_vec: Vec = used.into_iter().collect(); + used_vec.sort(); + + declared.rigid_vars.sort(); + declared.flex_vars.sort(); + + (declared, used_vec) +} + +fn variable_usage_help(con: &Constraint, declared: &mut SeenVariables, used: &mut ImSet) { + use Constraint::*; + + match con { + True | SaveTheEnvironment => (), + Eq(tipe, expectation, _) => { + for v in tipe.variables() { + used.insert(v); + } + + for v in expectation.get_type_ref().variables() { + used.insert(v); + } + } + Lookup(_, expectation, _) => { + for v in expectation.get_type_ref().variables() { + used.insert(v); + } + } + Pattern(_, _, tipe, pexpectation) => { + for v in tipe.variables() { + used.insert(v); + } + + for v in pexpectation.get_type_ref().variables() { + used.insert(v); + } + } + Let(letcon) => { + declared.rigid_vars.extend(letcon.rigid_vars.clone()); + declared.flex_vars.extend(letcon.flex_vars.clone()); + + variable_usage_help(&letcon.defs_constraint, declared, used); + variable_usage_help(&letcon.ret_constraint, declared, used); + } + And(constraints) => { + for sub in constraints { + variable_usage_help(sub, declared, used); + } + } + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 1046a9c40a..a0f44b1520 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,16 +1,33 @@ -extern crate roc; +extern crate roc_gen; -use roc::eval::Evaluated::*; -use roc::eval::{call, eval, Evaluated}; -use roc::expr::Expr; -use roc::parse; -use roc::region::{Located, Region}; +use crate::helpers::{infer_expr, uniq_expr_with}; +use bumpalo::Bump; +use inkwell::context::Context; +use inkwell::module::Linkage; +use inkwell::passes::PassManager; +use inkwell::types::BasicType; +use inkwell::OptimizationLevel; +use roc_collections::all::ImMap; +use roc_gen::llvm::build::{build_proc, build_proc_header, get_call_conventions}; +use roc_gen::llvm::convert::basic_type_from_layout; +use roc_mono::expr::{Expr, Procs}; +use roc_mono::layout::Layout; +use std::time::SystemTime; + +use inkwell::targets::{ + CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetTriple, +}; use std::fs::File; use std::io; use std::io::prelude::*; +use std::path::Path; +use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor}; -fn main() -> std::io::Result<()> { - let argv = std::env::args().into_iter().collect::>(); +pub mod helpers; + +fn main() -> io::Result<()> { + let now = SystemTime::now(); + let argv = std::env::args().collect::>(); match argv.get(1) { Some(filename) => { @@ -19,9 +36,15 @@ fn main() -> std::io::Result<()> { file.read_to_string(&mut contents)?; - let expr = parse::parse_string(contents.as_str()).unwrap(); + let dest_filename = Path::new(filename).with_extension("o"); - process_task(eval(expr)) + gen(contents.as_str(), Triple::host(), &dest_filename); + + let end_time = now.elapsed().unwrap(); + + println!("Finished in {} ms\n", end_time.as_millis()); + + Ok(()) } None => { println!("Usage: roc FILENAME.roc"); @@ -31,109 +54,213 @@ fn main() -> std::io::Result<()> { } } -fn process_task(evaluated: Evaluated) -> std::io::Result<()> { - match evaluated { - EvalError(region, problem) => { - println!( - "\n\u{001B}[4mruntime error\u{001B}[24m\n\n{} at {}\n", - format!("{}", problem), - format!("line {}, column {}", region.start_line, region.start_col) - ); +fn gen(src: &str, target: Triple, dest_filename: &Path) { + // Build the expr + let arena = Bump::new(); - Ok(()) - } - ApplyVariant(name, Some(mut vals)) => { - match name.as_str() { - "Echo" => { - // Extract the string from the Echo variant. - let string_to_be_displayed = match vals.pop() { - Some(Str(payload)) => payload, - Some(EvalError(region, err)) => { - panic!( - "RUNTIME ERROR in Echo: {} at {}", - format!("{}", err), - format!("line {}, column {}", region.start_line, region.start_col) - ); - } - Some(val) => { - panic!("TYPE MISMATCH in Echo: {}", format!("{}", val)); - } - None => { - panic!("TYPE MISMATCH in Echo: None"); - } - }; + let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = + uniq_expr_with(&arena, src, &ImMap::default()); - // Print the string to the console, since that's what Echo does! - println!("{}", string_to_be_displayed); + let mut unify_problems = Vec::new(); + let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); - // Continue with the callback. - let callback = vals.pop().unwrap(); + let context = Context::create(); + let module = context.create_module("app"); + let builder = context.create_builder(); + let fpm = PassManager::create(&module); - process_task(call( - Region { - start_line: 0, - start_col: 0, - end_line: 0, - end_col: 0, - }, - callback, - vec![with_zero_loc(Expr::EmptyRecord)], - )) - } - "Read" => { - // Read a line from from stdin, since that's what Read does! - let mut input = String::new(); + roc_gen::llvm::build::add_passes(&fpm); - io::stdin().read_line(&mut input)?; + fpm.initialize(); - // Continue with the callback. - let callback = vals.pop().unwrap(); + // Compute main_fn_type before moving subs to Env + let pointer_bytes = target.pointer_width().unwrap().bytes() as u32; + let layout = Layout::from_content(&arena, content, &subs, pointer_bytes) + .unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); - process_task(call( - Region { - start_line: 0, - start_col: 0, - end_line: 0, - end_col: 0, - }, - callback, - vec![with_zero_loc(Expr::Str(input.trim().to_string()))], - )) - } - "Success" => { - // We finished all our tasks. Great! No need to print anything. - Ok(()) - } - _ => { - // We don't recognize this variant, so display it and exit. - display_val(ApplyVariant(name, Some(vals))); + let execution_engine = module + .create_jit_execution_engine(OptimizationLevel::None) + .expect("Error creating JIT execution engine for test"); - Ok(()) - } - } - } - output => { - // We don't recognize this value, so display it and exit. - display_val(output); + let ptr_bytes = execution_engine + .get_target_data() + .get_pointer_byte_size(None); + let main_fn_type = + basic_type_from_layout(&arena, &context, &layout, ptr_bytes).fn_type(&[], false); + let main_fn_name = "$Test.main"; - Ok(()) + // Compile and add all the Procs before adding main + let mut env = roc_gen::llvm::build::Env { + arena: &arena, + builder: &builder, + context: &context, + interns, + module: arena.alloc(module), + ptr_bytes, + }; + let mut procs = Procs::default(); + let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); + + // Populate Procs and get the low-level Expr from the canonical Expr + let main_body = Expr::new( + &arena, + &mut subs, + loc_expr.value, + &mut procs, + home, + &mut ident_ids, + pointer_bytes, + ); + + // Put this module's ident_ids back in the interns, so we can use them in env. + env.interns.all_ident_ids.insert(home, ident_ids); + + let mut headers = Vec::with_capacity(procs.len()); + + // Add all the Proc headers to the module. + // We have to do this in a separate pass first, + // because their bodies may reference each other. + for (symbol, opt_proc) in procs.as_map().into_iter() { + if let Some(proc) = opt_proc { + let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc); + + headers.push((proc, fn_val, arg_basic_types)); } } -} -fn with_zero_loc(val: T) -> Located { - Located::new( - val, - Region { - start_line: 0, - start_col: 0, + // Build each proc using its header info. + for (proc, fn_val, arg_basic_types) in headers { + // NOTE: This is here to be uncommented in case verification fails. + // (This approach means we don't have to defensively clone name here.) + // + // println!("\n\nBuilding and then verifying function {}\n\n", name); + build_proc(&env, proc, &procs, fn_val, arg_basic_types); - end_line: 0, - end_col: 0, - }, - ) -} + if fn_val.verify(true) { + fpm.run_on(&fn_val); + } else { + // NOTE: If this fails, uncomment the above println to debug. + panic!( + "Non-main function failed LLVM verification. Uncomment the above println to debug!" + ); + } + } -fn display_val(evaluated: Evaluated) { - println!("\n\u{001B}[4mroc out\u{001B}[24m\n\n{}\n", evaluated); + // Add main to the module. + let cc = get_call_conventions(target.default_calling_convention().unwrap()); + let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); + + main_fn.set_call_conventions(cc); + main_fn.set_linkage(Linkage::External); + + // Add main's body + let basic_block = context.append_basic_block(main_fn, "entry"); + + builder.position_at_end(basic_block); + + let ret = roc_gen::llvm::build::build_expr( + &env, + &ImMap::default(), + main_fn, + &main_body, + &Procs::default(), + ); + + builder.build_return(Some(&ret)); + + // Uncomment this to see the module's un-optimized LLVM instruction output: + // env.module.print_to_stderr(); + + if main_fn.verify(true) { + fpm.run_on(&main_fn); + } else { + panic!("Function {} failed LLVM verification.", main_fn_name); + } + + // Verify the module + if let Err(errors) = env.module.verify() { + panic!("Errors defining module: {:?}", errors); + } + + // Uncomment this to see the module's optimized LLVM instruction output: + // env.module.print_to_stderr(); + + // Emit the .o file + + // NOTE: arch_str is *not* the same as the beginning of the magic target triple + // string! For example, if it's "x86-64" here, the magic target triple string + // will begin with "x86_64" (with an underscore) instead. + let arch_str = match target.architecture { + Architecture::X86_64 => { + Target::initialize_x86(&InitializationConfig::default()); + + "x86-64" + } + Architecture::Arm(_) => { + Target::initialize_arm(&InitializationConfig::default()); + + "arm" + } + Architecture::Wasm32 => { + Target::initialize_webassembly(&InitializationConfig::default()); + + "wasm32" + } + _ => panic!( + "TODO gracefully handle unsupported target architecture: {:?}", + target.architecture + ), + }; + + let opt = OptimizationLevel::Default; + let reloc = RelocMode::Default; + let model = CodeModel::Default; + + // Best guide I've found on how to determine these magic strings: + // + // https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures + let target_triple_str = match target { + Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Linux, + .. + } => "x86_64-unknown-linux-gnu", + Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Pc, + operating_system: OperatingSystem::Linux, + .. + } => "x86_64-pc-linux-gnu", + Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Darwin, + .. + } => "x86_64-unknown-darwin10", + Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Apple, + operating_system: OperatingSystem::Darwin, + .. + } => "x86_64-apple-darwin10", + _ => panic!("TODO gracefully handle unsupported target: {:?}", target), + }; + let target_machine = Target::from_name(arch_str) + .unwrap() + .create_target_machine( + &TargetTriple::create(target_triple_str), + arch_str, + "+avx2", // TODO this string was used uncritically from an example, and should be reexamined + opt, + reloc, + model, + ) + .unwrap(); + + target_machine + .write_to_file(&env.module, FileType::Object, &dest_filename) + .expect("Writing .o file failed"); + + println!("\nSuccess! 🎉\n\n\t➡ {}\n", dest_filename.display()); } diff --git a/compiler/gen/Cargo.toml b/compiler/gen/Cargo.toml index 5284f77506..8d4f1ce41d 100644 --- a/compiler/gen/Cargo.toml +++ b/compiler/gen/Cargo.toml @@ -38,6 +38,7 @@ inlinable_string = "0.1.0" # change the tag value in this Cargo.toml to point to that tag, and `cargo update`. # This way, GitHub Actions works and nobody's builds get broken. inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm8-0.release2" } +target-lexicon = "0.10" [dev-dependencies] roc_can = { path = "../can" } diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 5d0f3afb12..d9842790f6 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -3,6 +3,7 @@ use bumpalo::Bump; use inkwell::builder::Builder; use inkwell::context::Context; use inkwell::module::{Linkage, Module}; +use inkwell::passes::PassManager; use inkwell::types::{BasicTypeEnum, IntType, StructType}; use inkwell::values::BasicValueEnum::{self, *}; use inkwell::values::{FunctionValue, IntValue, PointerValue, StructValue}; @@ -15,6 +16,7 @@ use roc_collections::all::ImMap; use roc_module::symbol::{Interns, Symbol}; use roc_mono::expr::{Expr, Proc, Procs}; use roc_mono::layout::{Builtin, Layout}; +use target_lexicon::CallingConvention; /// This is for Inkwell's FunctionValue::verify - we want to know the verification /// output in debug builds, but we don't want it to print to stdout in release builds! @@ -45,6 +47,25 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { } } +pub fn add_passes(fpm: &PassManager>) { + // tail-call elimination is always on + fpm.add_instruction_combining_pass(); + fpm.add_tail_call_elimination_pass(); + + // Enable more optimizations when running cargo test --release + if !cfg!(debug_assertions) { + fpm.add_reassociate_pass(); + fpm.add_basic_alias_analysis_pass(); + fpm.add_promote_memory_to_register_pass(); + fpm.add_cfg_simplification_pass(); + fpm.add_gvn_pass(); + // TODO figure out why enabling any of these (even alone) causes LLVM to segfault + // fpm.add_strip_dead_prototypes_pass(); + // fpm.add_dead_arg_elimination_pass(); + // fpm.add_function_inlining_pass(); + } +} + #[allow(clippy::cognitive_complexity)] pub fn build_expr<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, @@ -1277,3 +1298,17 @@ fn list_set<'a, 'ctx, 'env>( ret_type.into(), ) } + +/// Translates a target_lexicon::Triple to a LLVM calling convention u32 +/// as described in https://llvm.org/doxygen/namespacellvm_1_1CallingConv.html +pub fn get_call_conventions(cc: CallingConvention) -> u32 { + use CallingConvention::*; + + // For now, we're returning 0 for the C calling convention on all of these. + // Not sure if we should be picking something more specific! + match cc { + SystemV => 0, + WasmBasicCAbi => 0, + WindowsFastcall => 0, + } +} diff --git a/compiler/gen/test.asm b/compiler/gen/test.asm new file mode 100644 index 0000000000..7c703ef496 --- /dev/null +++ b/compiler/gen/test.asm @@ -0,0 +1,4 @@ + .text + .file "my_module" + + .section ".note.GNU-stack","",@progbits diff --git a/compiler/gen/tests/helpers/eval.rs b/compiler/gen/tests/helpers/eval.rs index ef3562d769..eaa2dab95e 100644 --- a/compiler/gen/tests/helpers/eval.rs +++ b/compiler/gen/tests/helpers/eval.rs @@ -1,42 +1,8 @@ -// Pointer size on current system -pub const POINTER_SIZE: u32 = std::mem::size_of::() as u32; - -// 0 is the C calling convention - see https://llvm.org/doxygen/namespacellvm_1_1CallingConv.html -pub const MAIN_CALLING_CONVENTION: u32 = 0; - -#[macro_export] -macro_rules! get_fpm { - ($module:expr) => {{ - let fpm = PassManager::create(&$module); - - // tail-call elimination is always on - fpm.add_instruction_combining_pass(); - fpm.add_tail_call_elimination_pass(); - - // Enable more optimizations when running cargo test --release - if !cfg!(debug_assertions) { - fpm.add_reassociate_pass(); - fpm.add_basic_alias_analysis_pass(); - fpm.add_promote_memory_to_register_pass(); - fpm.add_cfg_simplification_pass(); - fpm.add_gvn_pass(); - // TODO figure out why enabling any of these (even alone) causes LLVM to segfault - // fpm.add_strip_dead_prototypes_pass(); - // fpm.add_dead_arg_elimination_pass(); - // fpm.add_function_inlining_pass(); - } - - fpm.initialize(); - - // TODO when should we call initialize, and then finalize? - - fpm - }}; -} - #[macro_export] macro_rules! assert_llvm_evals_to { ($src:expr, $expected:expr, $ty:ty, $transform:expr) => { + let target = target_lexicon::Triple::host(); + let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; let arena = Bump::new(); let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src); let subs = Subs::new(var_store.into()); @@ -46,18 +12,20 @@ macro_rules! assert_llvm_evals_to { let context = Context::create(); let module = context.create_module("app"); let builder = context.create_builder(); - let fpm = { get_fpm!(module) }; + let fpm = inkwell::passes::PassManager::create(&module); + + roc_gen::llvm::build::add_passes(&fpm); + + fpm.initialize(); // Compute main_fn_type before moving subs to Env - let layout = Layout::from_content(&arena, content, &subs, $crate::helpers::eval::POINTER_SIZE) + let layout = Layout::from_content(&arena, content, &subs, ptr_bytes) .unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); let execution_engine = module .create_jit_execution_engine(OptimizationLevel::None) .expect("Error creating JIT execution engine for test"); - let ptr_bytes = execution_engine.get_target_data().get_pointer_byte_size(None); - let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) .fn_type(&[], false); let main_fn_name = "$Test.main"; @@ -75,7 +43,7 @@ macro_rules! assert_llvm_evals_to { let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); // Populate Procs and get the low-level Expr from the canonical Expr - let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, $crate::helpers::eval::POINTER_SIZE); + let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, ptr_bytes); // Put this module's ident_ids back in the interns, so we can use them in Env. env.interns.all_ident_ids.insert(home, ident_ids); @@ -111,8 +79,9 @@ macro_rules! assert_llvm_evals_to { // Add main to the module. let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); + let cc = roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap()); - main_fn.set_call_conventions($crate::helpers::eval::MAIN_CALLING_CONVENTION); + main_fn.set_call_conventions(cc); // Add main's body let basic_block = context.append_basic_block(main_fn, "entry"); @@ -165,6 +134,8 @@ macro_rules! assert_llvm_evals_to { macro_rules! assert_opt_evals_to { ($src:expr, $expected:expr, $ty:ty, $transform:expr) => { let arena = Bump::new(); + let target = target_lexicon::Triple::host(); + let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr($src); let mut unify_problems = Vec::new(); @@ -173,10 +144,142 @@ macro_rules! assert_opt_evals_to { let context = Context::create(); let module = context.create_module("app"); let builder = context.create_builder(); - let fpm = { get_fpm!(module) }; + let fpm = PassManager::create(&module); + + roc_gen::llvm::build::add_passes(&fpm); + + fpm.initialize(); // Compute main_fn_type before moving subs to Env - let layout = Layout::from_content(&arena, content, &subs, $crate::helpers::eval::POINTER_SIZE) + let layout = Layout::from_content(&arena, content, &subs, ptr_bytes) + .unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); + + let execution_engine = + module + .create_jit_execution_engine(OptimizationLevel::None) + .expect("Error creating JIT execution engine for test"); + + let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) + .fn_type(&[], false); + let main_fn_name = "$Test.main"; + + // Compile and add all the Procs before adding main + let mut env = roc_gen::llvm::build::Env { + arena: &arena, + builder: &builder, + context: &context, + interns, + module: arena.alloc(module), + ptr_bytes + }; + let mut procs = Procs::default(); + let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); + + // Populate Procs and get the low-level Expr from the canonical Expr + let main_body = Expr::new(&arena, &mut subs, loc_expr.value, &mut procs, home, &mut ident_ids, ptr_bytes); + + // Put this module's ident_ids back in the interns, so we can use them in Env. + env.interns.all_ident_ids.insert(home, ident_ids); + + let mut headers = Vec::with_capacity(procs.len()); + + // Add all the Proc headers to the module. + // We have to do this in a separate pass first, + // because their bodies may reference each other. + for (symbol, opt_proc) in procs.as_map().into_iter() { + if let Some(proc) = opt_proc { + let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc); + + headers.push((proc, fn_val, arg_basic_types)); + } + } + + // Build each proc using its header info. + for (proc, fn_val, arg_basic_types) in headers { + // NOTE: This is here to be uncommented in case verification fails. + // (This approach means we don't have to defensively clone name here.) + // + // println!("\n\nBuilding and then verifying function {}\n\n", name); + build_proc(&env, proc, &procs, fn_val, arg_basic_types); + + if fn_val.verify(true) { + fpm.run_on(&fn_val); + } else { + // NOTE: If this fails, uncomment the above println to debug. + panic!("Non-main function failed LLVM verification. Uncomment the above println to debug!"); + } + } + + // Add main to the module. + let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); + let cc = roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap()); + + main_fn.set_call_conventions(cc); + + // Add main's body + let basic_block = context.append_basic_block(main_fn, "entry"); + + builder.position_at_end(basic_block); + + let ret = roc_gen::llvm::build::build_expr( + &env, + &ImMap::default(), + main_fn, + &main_body, + &mut Procs::default(), + ); + + builder.build_return(Some(&ret)); + + // Uncomment this to see the module's un-optimized LLVM instruction output: + // env.module.print_to_stderr(); + + if main_fn.verify(true) { + fpm.run_on(&main_fn); + } else { + panic!("Function {} failed LLVM verification.", main_fn_name); + } + + // Verify the module + if let Err(errors) = env.module.verify() { + panic!("Errors defining module: {:?}", errors); + } + + // Uncomment this to see the module's optimized LLVM instruction output: + // env.module.print_to_stderr(); + + unsafe { + let main: JitFunction $ty> = execution_engine + .get_function(main_fn_name) + .ok() + .ok_or(format!("Unable to JIT compile `{}`", main_fn_name)) + .expect("errored"); + + assert_eq!($transform(main.call()), $expected); + } + }; +} + +#[macro_export] +macro_rules! emit_expr { + ($src:expr, $expected:expr, $ty:ty, $transform:expr) => { + let arena = Bump::new(); + let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr($src); + + let mut unify_problems = Vec::new(); + let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); + + let context = Context::create(); + let module = context.create_module("app"); + let builder = context.create_builder(); + let fpm = PassManager::create(&module); + + roc_gen::llvm::build::add_passes(&fpm); + + fpm.initialize(); + + // Compute main_fn_type before moving subs to Env + let layout = Layout::from_content(&arena, content, &subs, ptr_bytes) .unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); let execution_engine = @@ -184,7 +287,6 @@ macro_rules! assert_opt_evals_to { .create_jit_execution_engine(OptimizationLevel::None) .expect("Error creating JIT execution engine for test"); - let ptr_bytes = execution_engine.get_target_data().get_pointer_byte_size(None); let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) .fn_type(&[], false); let main_fn_name = "$Test.main"; @@ -218,7 +320,6 @@ macro_rules! assert_opt_evals_to { headers.push((proc, fn_val, arg_basic_types)); } - } // Build each proc using its header info. diff --git a/compiler/mono/Cargo.toml b/compiler/mono/Cargo.toml index 67eae0c3aa..f2e3bb270b 100644 --- a/compiler/mono/Cargo.toml +++ b/compiler/mono/Cargo.toml @@ -11,12 +11,12 @@ roc_module = { path = "../module" } roc_types = { path = "../types" } roc_can = { path = "../can" } roc_unify = { path = "../unify" } +roc_problem = { path = "../problem" } bumpalo = { version = "3.2", features = ["collections"] } [dev-dependencies] roc_constrain = { path = "../constrain" } roc_builtins = { path = "../builtins" } -roc_problem = { path = "../problem" } roc_parse = { path = "../parse" } roc_solve = { path = "../solve" } pretty_assertions = "0.5.1 " diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index f3846d224b..a79bb8609f 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -340,31 +340,43 @@ fn pattern_to_when<'a>( // for underscore we generate a dummy Symbol (env.fresh_symbol(), body) } - - Shadowed(_, _) | UnsupportedPattern(_) => { - // create the runtime error here, instead of delegating to When. - // UnsupportedPattern should then never occcur in When - panic!("TODO generate runtime error here"); + Shadowed(region, loc_ident) => { + let error = roc_problem::can::RuntimeError::Shadowing { + original_region: *region, + shadow: loc_ident.clone(), + }; + (env.fresh_symbol(), Located::at_zero(RuntimeError(error))) } - AppliedTag {..} | RecordDestructure {..} => { + UnsupportedPattern(region) => { + // create the runtime error here, instead of delegating to When. + // UnsupportedPattern should then never occcur in When + let error = roc_problem::can::RuntimeError::UnsupportedPattern(*region); + (env.fresh_symbol(), Located::at_zero(RuntimeError(error))) + } + + AppliedTag { .. } | RecordDestructure { .. } => { let symbol = env.fresh_symbol(); let wrapped_body = When { cond_var: pattern_var, expr_var: body_var, loc_cond: Box::new(Located::at_zero(Var(symbol))), - branches: vec![WhenBranch{ patterns: vec![pattern], value: body, guard: None }], + branches: vec![WhenBranch { + patterns: vec![pattern], + value: body, + guard: None, + }], }; (symbol, Located::at_zero(wrapped_body)) } - // These patters are refutable, and thus should never occur outside a `when` expression - IntLiteral(_) | NumLiteral(_,_) | FloatLiteral(_) | StrLiteral(_) => { + IntLiteral(_) | NumLiteral(_, _) | FloatLiteral(_) | StrLiteral(_) => { + // These patters are refutable, and thus should never occur outside a `when` expression + // They should have been replaced with `UnsupportedPattern` during canonicalization unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value) } - } } @@ -1019,7 +1031,7 @@ fn from_can_when<'a>( if branches.is_empty() { // A when-expression with no branches is a runtime error. // We can't know what to return! - panic!("TODO compile a 0-branch when-expression to a RuntimeError"); + Expr::RuntimeError("Hit a 0-branch when expression") } else if branches.len() == 1 && branches[0].patterns.len() == 1 && branches[0].guard.is_none() { let first = branches.remove(0); diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 892b628f34..4ecb4aa0ce 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -34,6 +34,8 @@ pub enum RuntimeError { original_region: Region, shadow: Located, }, + // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! + UnsupportedPattern(Region), UnrecognizedFunctionName(Located), LookupNotInScope(Located), ValueNotExposed { diff --git a/compiler/solve/tests/test_solve.rs b/compiler/solve/tests/test_solve.rs index 1adbff44a2..74737921c9 100644 --- a/compiler/solve/tests/test_solve.rs +++ b/compiler/solve/tests/test_solve.rs @@ -2526,4 +2526,27 @@ mod test_solve { "List a -> List a", ); } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn rigid_record_quantification() { + // the ext here is qualified on the outside (because we have rank 1 types, not rank 2). + // That means e.g. `f : { bar : String, foo : Int } -> Bool }` is a valid argument. but + // that function could not be applied to the `{ foo : Int }` list. Therefore, this function + // is not allowed. + // + // should hit a debug_assert! in debug mode, and produce a type error in release mode + infer_eq_without_problem( + indoc!( + r#" + test : ({ foo : Int }ext -> Bool), { foo : Int } -> Bool + test = \fn, a -> fn a + + test + "# + ), + "should fail", + ); + } } diff --git a/examples/hello-world/.gitignore b/examples/hello-world/.gitignore new file mode 100644 index 0000000000..b9780edc19 --- /dev/null +++ b/examples/hello-world/.gitignore @@ -0,0 +1,3 @@ +hello +*.o +*.so diff --git a/examples/hello-world/README.md b/examples/hello-world/README.md new file mode 100644 index 0000000000..4add6731ed --- /dev/null +++ b/examples/hello-world/README.md @@ -0,0 +1,232 @@ +# Hello, World! + +Right now, there is only one way to build Roc programs: the Rube Goldberg Build Process. +(In the future, it will be nicer. At the moment, the nicer build system exists only +in our imaginations...so Rube Goldberg it is!) + +## Ingredients + +1. A host. For this example, our host is implemented in the file `host.rs`. +2. A Roc function. For this example, we'll use a function which returns the Roc string `"Hello, World!"` +3. Having `gcc` installed. This will not be necessary in the future, but the Rube Goldberg build process needs it. + +## Steps + +1. `cd` into `examples/hello-world/` +2. Run `cargo run hello.roc` to compile the Roc source code into a `hello.o` file. +3. Run `gcc -shared hello.o -o libhello_from_roc.so` to generate `libhello_from_roc.so`. (This filename must begin with `lib` and end in `.so` or else `host.rs` won't be able to find it!) +4. Move `libhello_from_roc.so` onto the system library path, e.g. with `sudo mv libhello_from_roc.so /usr/local/lib/` +5. Run `rustc host.rs -o hello` to generate the `hello` executable. +6. Run `./hello` to see the greeting! + +To run in release mode instead, do: + +```bash +cargo run --release hello.roc +``` + +## Design Notes + +This demonstrates the basic design of hosts: Roc code gets compiled into a pure +function (in this case, a thunk that always returns `"Hello, World!"`) and +then the host calls that function. Fundamentally, that's the whole idea! The host +might not even have a `main` - it could be a library, a plugin, anything. +Everything else is built on this basic "hosts calling linked pure functions" design. + +For example, things get more interesting when the compiled Roc function returns +a `Task` - that is, a tagged union data structure containing function pointers +to callback closures. This lets the Roc pure function describe arbitrary +chainable effects, which the host can interpret to perform I/O as requested by +the Roc program. (The tagged union `Task` would have a variant for each supported +I/O operation.) + +In this trivial example, it's very easy to line up the API between the host and +the Roc program. In a more involved host, this would be much trickier - especially +if the API were changing frequently during development. + +The idea there is to have a first-class concept of "glue code" which host authors +can write (it would be plain Roc code, but with some extra keywords that aren't +available in normal modules - kinda like `port module` in Elm), and which +describe both the Roc-host/C boundary as well as the Roc-host/Roc-app boundary. +Roc application authors only care about the Roc-host/Roc-app portion, and the +host author only cares about the Roc-host/C bounary when implementing the host. + +Using this glue code, the Roc compiler can generate C header files describing the +boundary. This not only gets us host compatibility with C compilers, but also +Rust FFI for free, because [`rust-bindgen`](https://github.com/rust-lang/rust-bindgen) +generates correct Rust FFI bindings from C headers. + +The whole "calling gcc and rustc" part of the current build process is obviously +not something Roc application authors should ever need to do. Rather, the idea +would be to have the host precompiled into an object file (eliminating the +need for Roc authors to run `rustc` in this example) and for the Roc compiler +to not only generate the object file for the Roc file, but also to link it with +the host object file to produce an executable (eliminating the need for `gcc`) +such that Roc application authors can concern themselves exclusively with Roc code +and need only the Roc compiler to build and to execute it. + +Of course, none of those niceties exist yet. But we'll get there! + +## The test that builds things + +```rust +let src = indoc!( + r#" + "Hello, World!" + "# +); + +// Build the expr +let arena = Bump::new(); +let (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr(src); + +let mut unify_problems = Vec::new(); +let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); + +let context = Context::create(); +let module = context.create_module("app"); +let builder = context.create_builder(); +let fpm = PassManager::create(&module); + +roc_gen::llvm::build::add_passes(&fpm); + +fpm.initialize(); + +// Compute main_fn_type before moving subs to Env +let layout = Layout::from_content(&arena, content, &subs, crate::helpers::eval::POINTER_SIZE) +.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs)); + +let execution_engine = module + .create_jit_execution_engine(OptimizationLevel::None) + .expect("Error creating JIT execution engine for test"); + +let ptr_bytes = execution_engine + .get_target_data() + .get_pointer_byte_size(None); +let main_fn_type = + basic_type_from_layout(&arena, &context, &layout, ptr_bytes).fn_type(&[], false); +let main_fn_name = "$Test.main"; + +// Compile and add all the Procs before adding main +let mut env = roc_gen::llvm::build::Env { + arena: &arena, + builder: &builder, + context: &context, + interns, + module: arena.alloc(module), + ptr_bytes, +}; +let mut procs = Procs::default(); +let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); + +// Populate Procs and get the low-level Expr from the canonical Expr +let main_body = Expr::new( + &arena, + &mut subs, + loc_expr.value, + &mut procs, + home, + &mut ident_ids, + crate::helpers::eval::POINTER_SIZE, +); + +// Put this module's ident_ids back in the interns, so we can use them in env. +env.interns.all_ident_ids.insert(home, ident_ids); + +let mut headers = Vec::with_capacity(procs.len()); + +// Add all the Proc headers to the module. +// We have to do this in a separate pass first, +// because their bodies may reference each other. +for (symbol, opt_proc) in procs.as_map().into_iter() { + if let Some(proc) = opt_proc { + let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc); + + headers.push((proc, fn_val, arg_basic_types)); + } +} + +// Build each proc using its header info. +for (proc, fn_val, arg_basic_types) in headers { + // NOTE: This is here to be uncommented in case verification fails. + // (This approach means we don't have to defensively clone name here.) + // + // println!("\n\nBuilding and then verifying function {}\n\n", name); + build_proc(&env, proc, &procs, fn_val, arg_basic_types); + + if fn_val.verify(true) { + fpm.run_on(&fn_val); + } else { + // NOTE: If this fails, uncomment the above println to debug. + panic!("Non-main function failed LLVM verification. Uncomment the above println to debug!"); + } +} + +// Add main to the module. +let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); + +main_fn.set_call_conventions(crate::helpers::eval::MAIN_CALLING_CONVENTION); +main_fn.set_linkage(Linkage::External); + +// Add main's body +let basic_block = context.append_basic_block(main_fn, "entry"); + +builder.position_at_end(basic_block); + +let ret = roc_gen::llvm::build::build_expr( + &env, + &ImMap::default(), + main_fn, + &main_body, + &mut Procs::default(), +); + +builder.build_return(Some(&ret)); + +// Uncomment this to see the module's un-optimized LLVM instruction output: +// env.module.print_to_stderr(); + +if main_fn.verify(true) { + fpm.run_on(&main_fn); +} else { + panic!("Function {} failed LLVM verification.", main_fn_name); +} + +// Verify the module +if let Err(errors) = env.module.verify() { + panic!("Errors defining module: {:?}", errors); +} + +// Uncomment this to see the module's optimized LLVM instruction output: +// env.module.print_to_stderr(); + +// Emit +Target::initialize_x86(&InitializationConfig::default()); + +let opt = OptimizationLevel::Default; +let reloc = RelocMode::Default; +let model = CodeModel::Default; +let target = Target::from_name("x86-64").unwrap(); +let target_machine = target + .create_target_machine( + &TargetTriple::create("x86_64-pc-linux-gnu"), + "x86-64", + "+avx2", + opt, + reloc, + model, + ) + .unwrap(); + +let path = Path::new("../../hello.o"); + +assert!(target_machine + .write_to_file(&env.module, FileType::Object, &path) + .is_ok()); + +let path = Path::new("../../hello.asm"); + +assert!(target_machine + .write_to_file(&env.module, FileType::Assembly, &path) + .is_ok()); +``` diff --git a/examples/hello-world/hello.roc b/examples/hello-world/hello.roc new file mode 100644 index 0000000000..b0d555865b --- /dev/null +++ b/examples/hello-world/hello.roc @@ -0,0 +1 @@ +"Hello, World!" diff --git a/examples/hello-world/host b/examples/hello-world/host new file mode 100755 index 0000000000..16cb34082a Binary files /dev/null and b/examples/hello-world/host differ diff --git a/examples/hello-world/host.rs b/examples/hello-world/host.rs new file mode 100644 index 0000000000..ce620637d0 --- /dev/null +++ b/examples/hello-world/host.rs @@ -0,0 +1,14 @@ +use std::ffi::CStr; +use std::os::raw::c_char; + +#[link(name = "hello_from_roc")] +extern "C" { + #[link_name = "$Test.main"] + fn str_from_roc() -> *const c_char; +} + +pub fn main() { + let c_str = unsafe { CStr::from_ptr(str_from_roc()) }; + + println!("Roc says: {}", c_str.to_str().unwrap()); +} diff --git a/hello b/hello new file mode 100755 index 0000000000..d1cec61d54 Binary files /dev/null and b/hello differ diff --git a/hello.asm b/hello.asm new file mode 100644 index 0000000000..c608634521 --- /dev/null +++ b/hello.asm @@ -0,0 +1,24 @@ + .text + .file "app" + .globl $Test.main + .p2align 4, 0x90 + .type $Test.main,@function +$Test.main: + .cfi_startproc + pushq %rax + .cfi_def_cfa_offset 16 + movl $14, %edi + callq malloc + movabsq $6278066737626506568, %rcx + movq %rcx, (%rax) + movl $1684828783, 8(%rax) + movw $33, 12(%rax) + popq %rcx + .cfi_def_cfa_offset 8 + retq +.Lfunc_end0: + .size $Test.main, .Lfunc_end0-($Test.main) + .cfi_endproc + + + .section ".note.GNU-stack","",@progbits diff --git a/hello.o b/hello.o new file mode 100644 index 0000000000..de6b3788b2 Binary files /dev/null and b/hello.o differ diff --git a/hello.so b/hello.so new file mode 100755 index 0000000000..42cca021e2 Binary files /dev/null and b/hello.so differ diff --git a/lib b/lib new file mode 100755 index 0000000000..53971be50e Binary files /dev/null and b/lib differ diff --git a/out.asm b/out.asm new file mode 100644 index 0000000000..6d2c4a86f1 --- /dev/null +++ b/out.asm @@ -0,0 +1,32 @@ + .text + .file "app" + .p2align 4, 0x90 + .type .L_0,@function +.L_0: + .cfi_startproc + movq %rdi, %rax + movq %rdi, %rdx + retq +.Lfunc_end0: + .size .L_0, .Lfunc_end0-.L_0 + .cfi_endproc + + .globl $Test.main + .p2align 4, 0x90 + .type $Test.main,@function +$Test.main: + .cfi_startproc + pushq %rax + .cfi_def_cfa_offset 16 + movl $9, %edi + callq .L_0 + imulq %rdx, %rax + popq %rcx + .cfi_def_cfa_offset 8 + retq +.Lfunc_end1: + .size $Test.main, .Lfunc_end1-($Test.main) + .cfi_endproc + + + .section ".note.GNU-stack","",@progbits diff --git a/out2.asm b/out2.asm new file mode 100644 index 0000000000..23fdd0bcff --- /dev/null +++ b/out2.asm @@ -0,0 +1,395 @@ +00000000 7F45 jg 0x47 +00000002 4C rex.wr +00000003 460201 add r8b,[rcx] +00000006 0100 add [rax],eax +00000008 0000 add [rax],al +0000000A 0000 add [rax],al +0000000C 0000 add [rax],al +0000000E 0000 add [rax],al +00000010 0100 add [rax],eax +00000012 3E0001 add [ds:rcx],al +00000015 0000 add [rax],al +00000017 0000 add [rax],al +00000019 0000 add [rax],al +0000001B 0000 add [rax],al +0000001D 0000 add [rax],al +0000001F 0000 add [rax],al +00000021 0000 add [rax],al +00000023 0000 add [rax],al +00000025 0000 add [rax],al +00000027 008001000000 add [rax+0x1],al +0000002D 0000 add [rax],al +0000002F 0000 add [rax],al +00000031 0000 add [rax],al +00000033 004000 add [rax+0x0],al +00000036 0000 add [rax],al +00000038 0000 add [rax],al +0000003A 400007 add [rdi],al +0000003D 0001 add [rcx],al +0000003F 004889 add [rax-0x77],cl +00000042 F8 clc +00000043 4889FA mov rdx,rdi +00000046 C3 ret +00000047 660F1F8400000000 nop word [rax+rax+0x0] + -00 +00000050 50 push rax +00000051 BF09000000 mov edi,0x9 +00000056 E8E5FFFFFF call 0x40 +0000005B 4801D0 add rax,rdx +0000005E 59 pop rcx +0000005F C3 ret +00000060 1400 adc al,0x0 +00000062 0000 add [rax],al +00000064 0000 add [rax],al +00000066 0000 add [rax],al +00000068 017A52 add [rdx+0x52],edi +0000006B 0001 add [rcx],al +0000006D 7810 js 0x7f +0000006F 011B add [rbx],ebx +00000071 0C07 or al,0x7 +00000073 089001000010 or [rax+0x10000001],dl +00000079 0000 add [rax],al +0000007B 001C00 add [rax+rax],bl +0000007E 0000 add [rax],al +00000080 0000 add [rax],al +00000082 0000 add [rax],al +00000084 07 db 0x07 +00000085 0000 add [rax],al +00000087 0000 add [rax],al +00000089 0000 add [rax],al +0000008B 0018 add [rax],bl +0000008D 0000 add [rax],al +0000008F 0030 add [rax],dh +00000091 0000 add [rax],al +00000093 0000 add [rax],al +00000095 0000 add [rax],al +00000097 0010 add [rax],dl +00000099 0000 add [rax],al +0000009B 0000 add [rax],al +0000009D 41 rex.b +0000009E 0E db 0x0e +0000009F 104E0E adc [rsi+0xe],cl +000000A2 0800 or [rax],al +000000A4 0000 add [rax],al +000000A6 0000 add [rax],al +000000A8 0000 add [rax],al +000000AA 0000 add [rax],al +000000AC 0000 add [rax],al +000000AE 0000 add [rax],al +000000B0 0000 add [rax],al +000000B2 0000 add [rax],al +000000B4 0000 add [rax],al +000000B6 0000 add [rax],al +000000B8 0000 add [rax],al +000000BA 0000 add [rax],al +000000BC 0000 add [rax],al +000000BE 0000 add [rax],al +000000C0 07 db 0x07 +000000C1 0000 add [rax],al +000000C3 000400 add [rax+rax],al +000000C6 F1 int1 +000000C7 FF00 inc dword [rax] +000000C9 0000 add [rax],al +000000CB 0000 add [rax],al +000000CD 0000 add [rax],al +000000CF 0000 add [rax],al +000000D1 0000 add [rax],al +000000D3 0000 add [rax],al +000000D5 0000 add [rax],al +000000D7 0000 add [rax],al +000000D9 0000 add [rax],al +000000DB 0003 add [rbx],al +000000DD 0002 add [rdx],al +000000DF 0000 add [rax],al +000000E1 0000 add [rax],al +000000E3 0000 add [rax],al +000000E5 0000 add [rax],al +000000E7 0000 add [rax],al +000000E9 0000 add [rax],al +000000EB 0000 add [rax],al +000000ED 0000 add [rax],al +000000EF 000B add [rbx],cl +000000F1 0000 add [rax],al +000000F3 0012 add [rdx],dl +000000F5 0002 add [rdx],al +000000F7 0010 add [rax],dl +000000F9 0000 add [rax],al +000000FB 0000 add [rax],al +000000FD 0000 add [rax],al +000000FF 0010 add [rax],dl +00000101 0000 add [rax],al +00000103 0000 add [rax],al +00000105 0000 add [rax],al +00000107 0020 add [rax],ah +00000109 0000 add [rax],al +0000010B 0000 add [rax],al +0000010D 0000 add [rax],al +0000010F 0002 add [rdx],al +00000111 0000 add [rax],al +00000113 0002 add [rdx],al +00000115 0000 add [rax],al +00000117 0000 add [rax],al +00000119 0000 add [rax],al +0000011B 0000 add [rax],al +0000011D 0000 add [rax],al +0000011F 003400 add [rax+rax],dh +00000122 0000 add [rax],al +00000124 0000 add [rax],al +00000126 0000 add [rax],al +00000128 0200 add al,[rax] +0000012A 0000 add [rax],al +0000012C 0200 add al,[rax] +0000012E 0000 add [rax],al +00000130 1000 adc [rax],al +00000132 0000 add [rax],al +00000134 0000 add [rax],al +00000136 0000 add [rax],al +00000138 002E add [rsi],ch +0000013A 7465 jz 0x1a1 +0000013C 7874 js 0x1b2 +0000013E 006170 add [rcx+0x70],ah +00000141 7000 jo 0x143 +00000143 2454 and al,0x54 +00000145 657374 gs jnc 0x1bc +00000148 2E6D cs insd +0000014A 61 db 0x61 +0000014B 696E002E6E6F74 imul ebp,[rsi+0x0],dword 0x746f6e2e +00000152 65 gs +00000153 2E cs +00000154 47 rex.rxb +00000155 4E55 push rbp +00000157 2D73746163 sub eax,0x63617473 +0000015C 6B002E imul eax,[rax],byte +0x2e +0000015F 7265 jc 0x1c6 +00000161 6C insb +00000162 61 db 0x61 +00000163 2E65685F667261 gs push qword 0x6172665f +0000016A 6D insd +0000016B 65002E add [gs:rsi],ch +0000016E 7374 jnc 0x1e4 +00000170 7274 jc 0x1e6 +00000172 61 db 0x61 +00000173 62 db 0x62 +00000174 002E add [rsi],ch +00000176 7379 jnc 0x1f1 +00000178 6D insd +00000179 7461 jz 0x1dc +0000017B 62 db 0x62 +0000017C 0000 add [rax],al +0000017E 0000 add [rax],al +00000180 0000 add [rax],al +00000182 0000 add [rax],al +00000184 0000 add [rax],al +00000186 0000 add [rax],al +00000188 0000 add [rax],al +0000018A 0000 add [rax],al +0000018C 0000 add [rax],al +0000018E 0000 add [rax],al +00000190 0000 add [rax],al +00000192 0000 add [rax],al +00000194 0000 add [rax],al +00000196 0000 add [rax],al +00000198 0000 add [rax],al +0000019A 0000 add [rax],al +0000019C 0000 add [rax],al +0000019E 0000 add [rax],al +000001A0 0000 add [rax],al +000001A2 0000 add [rax],al +000001A4 0000 add [rax],al +000001A6 0000 add [rax],al +000001A8 0000 add [rax],al +000001AA 0000 add [rax],al +000001AC 0000 add [rax],al +000001AE 0000 add [rax],al +000001B0 0000 add [rax],al +000001B2 0000 add [rax],al +000001B4 0000 add [rax],al +000001B6 0000 add [rax],al +000001B8 0000 add [rax],al +000001BA 0000 add [rax],al +000001BC 0000 add [rax],al +000001BE 0000 add [rax],al +000001C0 3500000003 xor eax,0x3000000 +000001C5 0000 add [rax],al +000001C7 0000 add [rax],al +000001C9 0000 add [rax],al +000001CB 0000 add [rax],al +000001CD 0000 add [rax],al +000001CF 0000 add [rax],al +000001D1 0000 add [rax],al +000001D3 0000 add [rax],al +000001D5 0000 add [rax],al +000001D7 0038 add [rax],bh +000001D9 0100 add [rax],eax +000001DB 0000 add [rax],al +000001DD 0000 add [rax],al +000001DF 004500 add [rbp+0x0],al +000001E2 0000 add [rax],al +000001E4 0000 add [rax],al +000001E6 0000 add [rax],al +000001E8 0000 add [rax],al +000001EA 0000 add [rax],al +000001EC 0000 add [rax],al +000001EE 0000 add [rax],al +000001F0 0100 add [rax],eax +000001F2 0000 add [rax],al +000001F4 0000 add [rax],al +000001F6 0000 add [rax],al +000001F8 0000 add [rax],al +000001FA 0000 add [rax],al +000001FC 0000 add [rax],al +000001FE 0000 add [rax],al +00000200 0100 add [rax],eax +00000202 0000 add [rax],al +00000204 0100 add [rax],eax +00000206 0000 add [rax],al +00000208 06 db 0x06 +00000209 0000 add [rax],al +0000020B 0000 add [rax],al +0000020D 0000 add [rax],al +0000020F 0000 add [rax],al +00000211 0000 add [rax],al +00000213 0000 add [rax],al +00000215 0000 add [rax],al +00000217 004000 add [rax+0x0],al +0000021A 0000 add [rax],al +0000021C 0000 add [rax],al +0000021E 0000 add [rax],al +00000220 2000 and [rax],al +00000222 0000 add [rax],al +00000224 0000 add [rax],al +00000226 0000 add [rax],al +00000228 0000 add [rax],al +0000022A 0000 add [rax],al +0000022C 0000 add [rax],al +0000022E 0000 add [rax],al +00000230 1000 adc [rax],al +00000232 0000 add [rax],al +00000234 0000 add [rax],al +00000236 0000 add [rax],al +00000238 0000 add [rax],al +0000023A 0000 add [rax],al +0000023C 0000 add [rax],al +0000023E 0000 add [rax],al +00000240 16 db 0x16 +00000241 0000 add [rax],al +00000243 0001 add [rcx],al +00000245 0000 add [rax],al +00000247 0000 add [rax],al +00000249 0000 add [rax],al +0000024B 0000 add [rax],al +0000024D 0000 add [rax],al +0000024F 0000 add [rax],al +00000251 0000 add [rax],al +00000253 0000 add [rax],al +00000255 0000 add [rax],al +00000257 006000 add [rax+0x0],ah +0000025A 0000 add [rax],al +0000025C 0000 add [rax],al +0000025E 0000 add [rax],al +00000260 0000 add [rax],al +00000262 0000 add [rax],al +00000264 0000 add [rax],al +00000266 0000 add [rax],al +00000268 0000 add [rax],al +0000026A 0000 add [rax],al +0000026C 0000 add [rax],al +0000026E 0000 add [rax],al +00000270 0100 add [rax],eax +00000272 0000 add [rax],al +00000274 0000 add [rax],al +00000276 0000 add [rax],al +00000278 0000 add [rax],al +0000027A 0000 add [rax],al +0000027C 0000 add [rax],al +0000027E 0000 add [rax],al +00000280 2B00 sub eax,[rax] +00000282 0000 add [rax],al +00000284 0100 add [rax],eax +00000286 007002 add [rax+0x2],dh +00000289 0000 add [rax],al +0000028B 0000 add [rax],al +0000028D 0000 add [rax],al +0000028F 0000 add [rax],al +00000291 0000 add [rax],al +00000293 0000 add [rax],al +00000295 0000 add [rax],al +00000297 006000 add [rax+0x0],ah +0000029A 0000 add [rax],al +0000029C 0000 add [rax],al +0000029E 0000 add [rax],al +000002A0 480000 o64 add [rax],al +000002A3 0000 add [rax],al +000002A5 0000 add [rax],al +000002A7 0000 add [rax],al +000002A9 0000 add [rax],al +000002AB 0000 add [rax],al +000002AD 0000 add [rax],al +000002AF 0008 add [rax],cl +000002B1 0000 add [rax],al +000002B3 0000 add [rax],al +000002B5 0000 add [rax],al +000002B7 0000 add [rax],al +000002B9 0000 add [rax],al +000002BB 0000 add [rax],al +000002BD 0000 add [rax],al +000002BF 0026 add [rsi],ah +000002C1 0000 add [rax],al +000002C3 000400 add [rax+rax],al +000002C6 0000 add [rax],al +000002C8 0000 add [rax],al +000002CA 0000 add [rax],al +000002CC 0000 add [rax],al +000002CE 0000 add [rax],al +000002D0 0000 add [rax],al +000002D2 0000 add [rax],al +000002D4 0000 add [rax],al +000002D6 0000 add [rax],al +000002D8 0801 or [rcx],al +000002DA 0000 add [rax],al +000002DC 0000 add [rax],al +000002DE 0000 add [rax],al +000002E0 3000 xor [rax],al +000002E2 0000 add [rax],al +000002E4 0000 add [rax],al +000002E6 0000 add [rax],al +000002E8 06 db 0x06 +000002E9 0000 add [rax],al +000002EB 000400 add [rax+rax],al +000002EE 0000 add [rax],al +000002F0 0800 or [rax],al +000002F2 0000 add [rax],al +000002F4 0000 add [rax],al +000002F6 0000 add [rax],al +000002F8 1800 sbb [rax],al +000002FA 0000 add [rax],al +000002FC 0000 add [rax],al +000002FE 0000 add [rax],al +00000300 3D00000002 cmp eax,0x2000000 +00000305 0000 add [rax],al +00000307 0000 add [rax],al +00000309 0000 add [rax],al +0000030B 0000 add [rax],al +0000030D 0000 add [rax],al +0000030F 0000 add [rax],al +00000311 0000 add [rax],al +00000313 0000 add [rax],al +00000315 0000 add [rax],al +00000317 00A800000000 add [rax+0x0],ch +0000031D 0000 add [rax],al +0000031F 006000 add [rax+0x0],ah +00000322 0000 add [rax],al +00000324 0000 add [rax],al +00000326 0000 add [rax],al +00000328 0100 add [rax],eax +0000032A 0000 add [rax],al +0000032C 0300 add eax,[rax] +0000032E 0000 add [rax],al +00000330 0800 or [rax],al +00000332 0000 add [rax],al +00000334 0000 add [rax],al +00000336 0000 add [rax],al +00000338 1800 sbb [rax],al +0000033A 0000 add [rax],al +0000033C 0000 add [rax],al +0000033E 0000 add [rax],al diff --git a/out2.o b/out2.o new file mode 100644 index 0000000000..d34ace88dd Binary files /dev/null and b/out2.o differ diff --git a/rust b/rust new file mode 100755 index 0000000000..cde50d8fdf Binary files /dev/null and b/rust differ diff --git a/rust.rs b/rust.rs new file mode 100644 index 0000000000..cd1dc619e8 --- /dev/null +++ b/rust.rs @@ -0,0 +1,13 @@ +pub fn foo(x: i64) -> (i64, i64) { + return (x, x); +} + +pub fn test_main() -> i64 { + let record = foo(0x9); + + record.0 * record.1 +} + +pub fn main() { + test_main(); +} diff --git a/rust.s b/rust.s new file mode 100644 index 0000000000..f70c1dc0fb --- /dev/null +++ b/rust.s @@ -0,0 +1,315 @@ + .text + .file "rust.7rcbfp3g-cgu.0" + .section .text._ZN3std2rt10lang_start17h10ea254a893e0692E,"ax",@progbits + .hidden _ZN3std2rt10lang_start17h10ea254a893e0692E + .globl _ZN3std2rt10lang_start17h10ea254a893e0692E + .p2align 4, 0x90 + .type _ZN3std2rt10lang_start17h10ea254a893e0692E,@function +_ZN3std2rt10lang_start17h10ea254a893e0692E: + .cfi_startproc + subq $40, %rsp + .cfi_def_cfa_offset 48 + leaq .L__unnamed_1(%rip), %rax + movq %rdi, 32(%rsp) + leaq 32(%rsp), %rcx + movq %rcx, %rdi + movq %rsi, 24(%rsp) + movq %rax, %rsi + movq 24(%rsp), %rax + movq %rdx, 16(%rsp) + movq %rax, %rdx + movq 16(%rsp), %rcx + callq *_ZN3std2rt19lang_start_internal17h9cf8802361ad86c2E@GOTPCREL(%rip) + movq %rax, 8(%rsp) + movq 8(%rsp), %rax + addq $40, %rsp + .cfi_def_cfa_offset 8 + retq +.Lfunc_end0: + .size _ZN3std2rt10lang_start17h10ea254a893e0692E, .Lfunc_end0-_ZN3std2rt10lang_start17h10ea254a893e0692E + .cfi_endproc + + .section ".text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE","ax",@progbits + .p2align 4, 0x90 + .type _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE,@function +_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE: + .cfi_startproc + pushq %rax + .cfi_def_cfa_offset 16 + callq *(%rdi) + callq _ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h4c1d7168636eac2bE + movl %eax, 4(%rsp) + movl 4(%rsp), %eax + popq %rcx + .cfi_def_cfa_offset 8 + retq +.Lfunc_end1: + .size _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE, .Lfunc_end1-_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE + .cfi_endproc + + .section .text._ZN3std3sys4unix7process14process_common8ExitCode6as_i3217hafc7a17b3af818e6E,"ax",@progbits + .p2align 4, 0x90 + .type _ZN3std3sys4unix7process14process_common8ExitCode6as_i3217hafc7a17b3af818e6E,@function +_ZN3std3sys4unix7process14process_common8ExitCode6as_i3217hafc7a17b3af818e6E: + .cfi_startproc + movzbl (%rdi), %eax + retq +.Lfunc_end2: + .size _ZN3std3sys4unix7process14process_common8ExitCode6as_i3217hafc7a17b3af818e6E, .Lfunc_end2-_ZN3std3sys4unix7process14process_common8ExitCode6as_i3217hafc7a17b3af818e6E + .cfi_endproc + + .section ".text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h3c0881b3435f345aE","ax",@progbits + .p2align 4, 0x90 + .type _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h3c0881b3435f345aE,@function +_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h3c0881b3435f345aE: + .cfi_startproc + subq $24, %rsp + .cfi_def_cfa_offset 32 + movq (%rdi), %rdi + callq _ZN4core3ops8function6FnOnce9call_once17h95777eb19c893e7cE + movl %eax, 12(%rsp) + movl 12(%rsp), %eax + addq $24, %rsp + .cfi_def_cfa_offset 8 + retq +.Lfunc_end3: + .size _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h3c0881b3435f345aE, .Lfunc_end3-_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h3c0881b3435f345aE + .cfi_endproc + + .section .text._ZN4core3ops8function6FnOnce9call_once17h95777eb19c893e7cE,"ax",@progbits + .p2align 4, 0x90 + .type _ZN4core3ops8function6FnOnce9call_once17h95777eb19c893e7cE,@function +_ZN4core3ops8function6FnOnce9call_once17h95777eb19c893e7cE: +.Lfunc_begin0: + .cfi_startproc + .cfi_personality 155, DW.ref.rust_eh_personality + .cfi_lsda 27, .Lexception0 + subq $40, %rsp + .cfi_def_cfa_offset 48 + movq %rdi, 8(%rsp) +.Ltmp0: + leaq 8(%rsp), %rdi + callq _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE +.Ltmp1: + movl %eax, 4(%rsp) + jmp .LBB4_1 +.LBB4_1: + jmp .LBB4_2 +.LBB4_2: + movl 4(%rsp), %eax + addq $40, %rsp + .cfi_def_cfa_offset 8 + retq +.LBB4_3: + .cfi_def_cfa_offset 48 + jmp .LBB4_4 +.LBB4_4: + movq 24(%rsp), %rdi + callq _Unwind_Resume@PLT + ud2 +.LBB4_5: +.Ltmp2: + movq %rax, 24(%rsp) + movl %edx, 32(%rsp) + jmp .LBB4_3 +.Lfunc_end4: + .size _ZN4core3ops8function6FnOnce9call_once17h95777eb19c893e7cE, .Lfunc_end4-_ZN4core3ops8function6FnOnce9call_once17h95777eb19c893e7cE + .cfi_endproc + .section .gcc_except_table,"a",@progbits + .p2align 2 +GCC_except_table4: +.Lexception0: + .byte 255 + .byte 255 + .byte 1 + .uleb128 .Lcst_end0-.Lcst_begin0 +.Lcst_begin0: + .uleb128 .Ltmp0-.Lfunc_begin0 + .uleb128 .Ltmp1-.Ltmp0 + .uleb128 .Ltmp2-.Lfunc_begin0 + .byte 0 + .uleb128 .Ltmp1-.Lfunc_begin0 + .uleb128 .Lfunc_end4-.Ltmp1 + .byte 0 + .byte 0 +.Lcst_end0: + .p2align 2 + + .section .text._ZN4core3ptr13drop_in_place17h5ea43678993f7f74E,"ax",@progbits + .p2align 4, 0x90 + .type _ZN4core3ptr13drop_in_place17h5ea43678993f7f74E,@function +_ZN4core3ptr13drop_in_place17h5ea43678993f7f74E: + .cfi_startproc + retq +.Lfunc_end5: + .size _ZN4core3ptr13drop_in_place17h5ea43678993f7f74E, .Lfunc_end5-_ZN4core3ptr13drop_in_place17h5ea43678993f7f74E + .cfi_endproc + + .section ".text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h4c1d7168636eac2bE","ax",@progbits + .p2align 4, 0x90 + .type _ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h4c1d7168636eac2bE,@function +_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h4c1d7168636eac2bE: + .cfi_startproc + pushq %rax + .cfi_def_cfa_offset 16 + xorl %edi, %edi + callq _ZN68_$LT$std..process..ExitCode$u20$as$u20$std..process..Termination$GT$6report17h4641d8c7c31fa834E + movl %eax, 4(%rsp) + movl 4(%rsp), %eax + popq %rcx + .cfi_def_cfa_offset 8 + retq +.Lfunc_end6: + .size _ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h4c1d7168636eac2bE, .Lfunc_end6-_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h4c1d7168636eac2bE + .cfi_endproc + + .section ".text._ZN68_$LT$std..process..ExitCode$u20$as$u20$std..process..Termination$GT$6report17h4641d8c7c31fa834E","ax",@progbits + .p2align 4, 0x90 + .type _ZN68_$LT$std..process..ExitCode$u20$as$u20$std..process..Termination$GT$6report17h4641d8c7c31fa834E,@function +_ZN68_$LT$std..process..ExitCode$u20$as$u20$std..process..Termination$GT$6report17h4641d8c7c31fa834E: + .cfi_startproc + pushq %rax + .cfi_def_cfa_offset 16 + movb %dil, 7(%rsp) + leaq 7(%rsp), %rdi + callq _ZN3std3sys4unix7process14process_common8ExitCode6as_i3217hafc7a17b3af818e6E + movl %eax, (%rsp) + movl (%rsp), %eax + popq %rcx + .cfi_def_cfa_offset 8 + retq +.Lfunc_end7: + .size _ZN68_$LT$std..process..ExitCode$u20$as$u20$std..process..Termination$GT$6report17h4641d8c7c31fa834E, .Lfunc_end7-_ZN68_$LT$std..process..ExitCode$u20$as$u20$std..process..Termination$GT$6report17h4641d8c7c31fa834E + .cfi_endproc + + .section .text._ZN4rust3foo17h24346e1203f74a2dE,"ax",@progbits + .p2align 4, 0x90 + .type _ZN4rust3foo17h24346e1203f74a2dE,@function +_ZN4rust3foo17h24346e1203f74a2dE: + .cfi_startproc + subq $16, %rsp + .cfi_def_cfa_offset 24 + movq %rdi, (%rsp) + movq %rdi, 8(%rsp) + movq (%rsp), %rax + movq 8(%rsp), %rdx + addq $16, %rsp + .cfi_def_cfa_offset 8 + retq +.Lfunc_end8: + .size _ZN4rust3foo17h24346e1203f74a2dE, .Lfunc_end8-_ZN4rust3foo17h24346e1203f74a2dE + .cfi_endproc + + .section .text._ZN4rust9test_main17h45325f9e1ca54b4bE,"ax",@progbits + .p2align 4, 0x90 + .type _ZN4rust9test_main17h45325f9e1ca54b4bE,@function +_ZN4rust9test_main17h45325f9e1ca54b4bE: + .cfi_startproc + subq $24, %rsp + .cfi_def_cfa_offset 32 + movl $9, %edi + callq _ZN4rust3foo17h24346e1203f74a2dE + movq %rax, 16(%rsp) + movq %rdx, 8(%rsp) + movq 16(%rsp), %rax + movq 8(%rsp), %rcx + imulq %rcx, %rax + seto %dl + testb $1, %dl + movq %rax, (%rsp) + jne .LBB9_3 + movq (%rsp), %rax + addq $24, %rsp + .cfi_def_cfa_offset 8 + retq +.LBB9_3: + .cfi_def_cfa_offset 32 + leaq str.0(%rip), %rdi + leaq .L__unnamed_2(%rip), %rdx + movq _ZN4core9panicking5panic17hcdc9f0ba8d71d265E@GOTPCREL(%rip), %rax + movl $33, %esi + callq *%rax + ud2 +.Lfunc_end9: + .size _ZN4rust9test_main17h45325f9e1ca54b4bE, .Lfunc_end9-_ZN4rust9test_main17h45325f9e1ca54b4bE + .cfi_endproc + + .section .text._ZN4rust4main17h523dcf5432fcfd88E,"ax",@progbits + .p2align 4, 0x90 + .type _ZN4rust4main17h523dcf5432fcfd88E,@function +_ZN4rust4main17h523dcf5432fcfd88E: + .cfi_startproc + pushq %rax + .cfi_def_cfa_offset 16 + callq _ZN4rust9test_main17h45325f9e1ca54b4bE + popq %rax + .cfi_def_cfa_offset 8 + retq +.Lfunc_end10: + .size _ZN4rust4main17h523dcf5432fcfd88E, .Lfunc_end10-_ZN4rust4main17h523dcf5432fcfd88E + .cfi_endproc + + .section .text.main,"ax",@progbits + .globl main + .p2align 4, 0x90 + .type main,@function +main: + .cfi_startproc + pushq %rax + .cfi_def_cfa_offset 16 + movslq %edi, %rax + leaq _ZN4rust4main17h523dcf5432fcfd88E(%rip), %rdi + movq %rsi, (%rsp) + movq %rax, %rsi + movq (%rsp), %rdx + callq _ZN3std2rt10lang_start17h10ea254a893e0692E + popq %rcx + .cfi_def_cfa_offset 8 + retq +.Lfunc_end11: + .size main, .Lfunc_end11-main + .cfi_endproc + + .type .L__unnamed_1,@object + .section .data.rel.ro..L__unnamed_1,"aw",@progbits + .p2align 3 +.L__unnamed_1: + .quad _ZN4core3ptr13drop_in_place17h5ea43678993f7f74E + .quad 8 + .quad 8 + .quad _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE + .quad _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17haa4f2d7d298b0acfE + .quad _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h3c0881b3435f345aE + .size .L__unnamed_1, 48 + + .type .L__unnamed_3,@object + .section .rodata..L__unnamed_3,"a",@progbits +.L__unnamed_3: + .ascii "rust.rs" + .size .L__unnamed_3, 7 + + .type .L__unnamed_2,@object + .section .data.rel.ro..L__unnamed_2,"aw",@progbits + .p2align 3 +.L__unnamed_2: + .quad .L__unnamed_3 + .asciz "\007\000\000\000\000\000\000\000\b\000\000\000\005\000\000" + .size .L__unnamed_2, 24 + + .type str.0,@object + .section .rodata.str.0,"a",@progbits + .p2align 4 +str.0: + .ascii "attempt to multiply with overflow" + .size str.0, 33 + + .hidden DW.ref.rust_eh_personality + .weak DW.ref.rust_eh_personality + .section .data.DW.ref.rust_eh_personality,"aGw",@progbits,DW.ref.rust_eh_personality,comdat + .p2align 3 + .type DW.ref.rust_eh_personality,@object + .size DW.ref.rust_eh_personality, 8 +DW.ref.rust_eh_personality: + .quad rust_eh_personality + + .section ".note.GNU-stack","",@progbits