Merge remote-tracking branch 'origin/trunk' into report-problems

This commit is contained in:
Richard Feldman 2020-03-30 23:49:30 -04:00
commit 84ec100208
32 changed files with 2792 additions and 161 deletions

804
2 Normal file
View file

@ -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");
}
}

View file

@ -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`. 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.

33
Cargo.lock generated
View file

@ -526,6 +526,32 @@ version = "0.6.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 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]] [[package]]
name = "roc_builtins" name = "roc_builtins"
version = "0.1.0" version = "0.1.0"
@ -642,6 +668,7 @@ dependencies = [
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"roc_uniq", "roc_uniq",
"target-lexicon",
"tokio", "tokio",
] ]
@ -904,6 +931,12 @@ dependencies = [
"unicode-xid 0.2.0", "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]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"

View file

@ -19,5 +19,7 @@ members = [
"compiler/load", "compiler/load",
"compiler/gen", "compiler/gen",
"vendor/ena", "vendor/ena",
"vendor/pathfinding" "vendor/pathfinding",
"cli"
] ]

BIN
a.out Executable file

Binary file not shown.

View file

@ -8,6 +8,50 @@ keywords = ["roc", "gui"]
edition = "2018" edition = "2018"
description = "A CLI for Roc" description = "A CLI for Roc"
license = "Apache-2.0" license = "Apache-2.0"
default-run = "roc"
[[bin]]
name = "roc"
path = "src/main.rs"
test = false
bench = false
[dependencies] [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"

396
cli/src/helpers.rs Normal file
View file

@ -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<roc_types::types::Problem>,
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<ast::Expr<'a>, 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<Located<ast::Expr<'a>>, 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<Expr>,
Output,
Vec<Problem>,
Subs,
Variable,
Constraint,
ModuleId,
Interns,
) {
let declared_idents: &ImMap<Ident, (Symbol, Region)> = &ImMap::default();
uniq_expr_with(&Bump::new(), expr_str, declared_idents)
}
pub fn uniq_expr_with(
arena: &Bump,
expr_str: &str,
declared_idents: &ImMap<Ident, (Symbol, Region)>,
) -> (
Located<Expr>,
Output,
Vec<Problem>,
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<Expr>,
pub output: Output,
pub problems: Vec<Problem>,
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<K, V, I>(pairs: I) -> MutMap<K, V>
where
I: IntoIterator<Item = (K, V)>,
K: Hash + Eq,
{
let mut answer = MutMap::default();
for (key, value) in pairs {
answer.insert(key, value);
}
answer
}
pub fn im_map_from_pairs<K, V, I>(pairs: I) -> ImMap<K, V>
where
I: IntoIterator<Item = (K, V)>,
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<V, I>(elems: I) -> SendSet<V>
where
I: IntoIterator<Item = V>,
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<Variable> = used.into();
let mut decl: ImSet<Variable> = 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<Variable>,
pub flex_vars: Vec<Variable>,
}
pub fn variable_usage(con: &Constraint) -> (SeenVariables, Vec<Variable>) {
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<Variable> = 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<Variable>) {
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);
}
}
}
}

View file

@ -1,16 +1,33 @@
extern crate roc; extern crate roc_gen;
use roc::eval::Evaluated::*; use crate::helpers::{infer_expr, uniq_expr_with};
use roc::eval::{call, eval, Evaluated}; use bumpalo::Bump;
use roc::expr::Expr; use inkwell::context::Context;
use roc::parse; use inkwell::module::Linkage;
use roc::region::{Located, Region}; 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::fs::File;
use std::io; use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::Path;
use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor};
fn main() -> std::io::Result<()> { pub mod helpers;
let argv = std::env::args().into_iter().collect::<Vec<String>>();
fn main() -> io::Result<()> {
let now = SystemTime::now();
let argv = std::env::args().collect::<Vec<String>>();
match argv.get(1) { match argv.get(1) {
Some(filename) => { Some(filename) => {
@ -19,9 +36,15 @@ fn main() -> std::io::Result<()> {
file.read_to_string(&mut contents)?; 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 => { None => {
println!("Usage: roc FILENAME.roc"); println!("Usage: roc FILENAME.roc");
@ -31,109 +54,213 @@ fn main() -> std::io::Result<()> {
} }
} }
fn process_task(evaluated: Evaluated) -> std::io::Result<()> { fn gen(src: &str, target: Triple, dest_filename: &Path) {
match evaluated { // Build the expr
EvalError(region, problem) => { let arena = Bump::new();
println!(
"\n\u{001B}[4mruntime error\u{001B}[24m\n\n{} at {}\n",
format!("{}", problem),
format!("line {}, column {}", region.start_line, region.start_col)
);
Ok(()) let (loc_expr, _output, _problems, subs, var, constraint, home, interns) =
} uniq_expr_with(&arena, src, &ImMap::default());
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");
}
};
// Print the string to the console, since that's what Echo does! let mut unify_problems = Vec::new();
println!("{}", string_to_be_displayed); let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
// Continue with the callback. let context = Context::create();
let callback = vals.pop().unwrap(); let module = context.create_module("app");
let builder = context.create_builder();
let fpm = PassManager::create(&module);
process_task(call( roc_gen::llvm::build::add_passes(&fpm);
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();
io::stdin().read_line(&mut input)?; fpm.initialize();
// Continue with the callback. // Compute main_fn_type before moving subs to Env
let callback = vals.pop().unwrap(); 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( let execution_engine = module
Region { .create_jit_execution_engine(OptimizationLevel::None)
start_line: 0, .expect("Error creating JIT execution engine for test");
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)));
Ok(()) let ptr_bytes = execution_engine
} .get_target_data()
} .get_pointer_byte_size(None);
} let main_fn_type =
output => { basic_type_from_layout(&arena, &context, &layout, ptr_bytes).fn_type(&[], false);
// We don't recognize this value, so display it and exit. let main_fn_name = "$Test.main";
display_val(output);
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<T>(val: T) -> Located<T> { // Build each proc using its header info.
Located::new( for (proc, fn_val, arg_basic_types) in headers {
val, // NOTE: This is here to be uncommented in case verification fails.
Region { // (This approach means we don't have to defensively clone name here.)
start_line: 0, //
start_col: 0, // println!("\n\nBuilding and then verifying function {}\n\n", name);
build_proc(&env, proc, &procs, fn_val, arg_basic_types);
end_line: 0, if fn_val.verify(true) {
end_col: 0, 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) { // Add main to the module.
println!("\n\u{001B}[4mroc out\u{001B}[24m\n\n{}\n", evaluated); 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());
} }

View file

@ -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`. # 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. # This way, GitHub Actions works and nobody's builds get broken.
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm8-0.release2" } inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm8-0.release2" }
target-lexicon = "0.10"
[dev-dependencies] [dev-dependencies]
roc_can = { path = "../can" } roc_can = { path = "../can" }

View file

@ -3,6 +3,7 @@ use bumpalo::Bump;
use inkwell::builder::Builder; use inkwell::builder::Builder;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::module::{Linkage, Module}; use inkwell::module::{Linkage, Module};
use inkwell::passes::PassManager;
use inkwell::types::{BasicTypeEnum, IntType, StructType}; use inkwell::types::{BasicTypeEnum, IntType, StructType};
use inkwell::values::BasicValueEnum::{self, *}; use inkwell::values::BasicValueEnum::{self, *};
use inkwell::values::{FunctionValue, IntValue, PointerValue, StructValue}; use inkwell::values::{FunctionValue, IntValue, PointerValue, StructValue};
@ -15,6 +16,7 @@ use roc_collections::all::ImMap;
use roc_module::symbol::{Interns, Symbol}; use roc_module::symbol::{Interns, Symbol};
use roc_mono::expr::{Expr, Proc, Procs}; use roc_mono::expr::{Expr, Proc, Procs};
use roc_mono::layout::{Builtin, Layout}; use roc_mono::layout::{Builtin, Layout};
use target_lexicon::CallingConvention;
/// This is for Inkwell's FunctionValue::verify - we want to know the verification /// 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! /// 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<FunctionValue<'_>>) {
// 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)] #[allow(clippy::cognitive_complexity)]
pub fn build_expr<'a, 'ctx, 'env>( pub fn build_expr<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
@ -1277,3 +1298,17 @@ fn list_set<'a, 'ctx, 'env>(
ret_type.into(), 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,
}
}

4
compiler/gen/test.asm Normal file
View file

@ -0,0 +1,4 @@
.text
.file "my_module"
.section ".note.GNU-stack","",@progbits

View file

@ -1,42 +1,8 @@
// Pointer size on current system
pub const POINTER_SIZE: u32 = std::mem::size_of::<usize>() 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_export]
macro_rules! assert_llvm_evals_to { macro_rules! assert_llvm_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr) => { ($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 arena = Bump::new();
let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src); let CanExprOut { loc_expr, var_store, var, constraint, home, interns, .. } = can_expr($src);
let subs = Subs::new(var_store.into()); let subs = Subs::new(var_store.into());
@ -46,18 +12,20 @@ macro_rules! assert_llvm_evals_to {
let context = Context::create(); let context = Context::create();
let module = context.create_module("app"); let module = context.create_module("app");
let builder = context.create_builder(); 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 // 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)); .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 = let execution_engine =
module module
.create_jit_execution_engine(OptimizationLevel::None) .create_jit_execution_engine(OptimizationLevel::None)
.expect("Error creating JIT execution engine for test"); .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) let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes)
.fn_type(&[], false); .fn_type(&[], false);
let main_fn_name = "$Test.main"; 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(); let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
// Populate Procs and get the low-level Expr from the canonical Expr // 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. // 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); env.interns.all_ident_ids.insert(home, ident_ids);
@ -111,8 +79,9 @@ macro_rules! assert_llvm_evals_to {
// Add main to the module. // Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); 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 // Add main's body
let basic_block = context.append_basic_block(main_fn, "entry"); 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 { macro_rules! assert_opt_evals_to {
($src:expr, $expected:expr, $ty:ty, $transform:expr) => { ($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
let arena = Bump::new(); 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 (loc_expr, _output, _problems, subs, var, constraint, home, interns) = uniq_expr($src);
let mut unify_problems = Vec::new(); let mut unify_problems = Vec::new();
@ -173,10 +144,142 @@ macro_rules! assert_opt_evals_to {
let context = Context::create(); let context = Context::create();
let module = context.create_module("app"); let module = context.create_module("app");
let builder = context.create_builder(); 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 // 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<unsafe extern "C" fn() -> $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)); .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 = let execution_engine =
@ -184,7 +287,6 @@ macro_rules! assert_opt_evals_to {
.create_jit_execution_engine(OptimizationLevel::None) .create_jit_execution_engine(OptimizationLevel::None)
.expect("Error creating JIT execution engine for test"); .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) let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes)
.fn_type(&[], false); .fn_type(&[], false);
let main_fn_name = "$Test.main"; let main_fn_name = "$Test.main";
@ -218,7 +320,6 @@ macro_rules! assert_opt_evals_to {
headers.push((proc, fn_val, arg_basic_types)); headers.push((proc, fn_val, arg_basic_types));
} }
} }
// Build each proc using its header info. // Build each proc using its header info.

View file

@ -11,12 +11,12 @@ roc_module = { path = "../module" }
roc_types = { path = "../types" } roc_types = { path = "../types" }
roc_can = { path = "../can" } roc_can = { path = "../can" }
roc_unify = { path = "../unify" } roc_unify = { path = "../unify" }
roc_problem = { path = "../problem" }
bumpalo = { version = "3.2", features = ["collections"] } bumpalo = { version = "3.2", features = ["collections"] }
[dev-dependencies] [dev-dependencies]
roc_constrain = { path = "../constrain" } roc_constrain = { path = "../constrain" }
roc_builtins = { path = "../builtins" } roc_builtins = { path = "../builtins" }
roc_problem = { path = "../problem" }
roc_parse = { path = "../parse" } roc_parse = { path = "../parse" }
roc_solve = { path = "../solve" } roc_solve = { path = "../solve" }
pretty_assertions = "0.5.1 " pretty_assertions = "0.5.1 "

View file

@ -340,31 +340,43 @@ fn pattern_to_when<'a>(
// for underscore we generate a dummy Symbol // for underscore we generate a dummy Symbol
(env.fresh_symbol(), body) (env.fresh_symbol(), body)
} }
Shadowed(region, loc_ident) => {
Shadowed(_, _) | UnsupportedPattern(_) => { let error = roc_problem::can::RuntimeError::Shadowing {
// create the runtime error here, instead of delegating to When. original_region: *region,
// UnsupportedPattern should then never occcur in When shadow: loc_ident.clone(),
panic!("TODO generate runtime error here"); };
(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 symbol = env.fresh_symbol();
let wrapped_body = When { let wrapped_body = When {
cond_var: pattern_var, cond_var: pattern_var,
expr_var: body_var, expr_var: body_var,
loc_cond: Box::new(Located::at_zero(Var(symbol))), 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)) (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) 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() { if branches.is_empty() {
// A when-expression with no branches is a runtime error. // A when-expression with no branches is a runtime error.
// We can't know what to return! // 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() } else if branches.len() == 1 && branches[0].patterns.len() == 1 && branches[0].guard.is_none()
{ {
let first = branches.remove(0); let first = branches.remove(0);

View file

@ -34,6 +34,8 @@ pub enum RuntimeError {
original_region: Region, original_region: Region,
shadow: Located<Ident>, shadow: Located<Ident>,
}, },
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
UnsupportedPattern(Region),
UnrecognizedFunctionName(Located<InlinableString>), UnrecognizedFunctionName(Located<InlinableString>),
LookupNotInScope(Located<InlinableString>), LookupNotInScope(Located<InlinableString>),
ValueNotExposed { ValueNotExposed {

View file

@ -2526,4 +2526,27 @@ mod test_solve {
"List a -> List a", "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",
);
}
} }

3
examples/hello-world/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
hello
*.o
*.so

View file

@ -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());
```

View file

@ -0,0 +1 @@
"Hello, World!"

BIN
examples/hello-world/host Executable file

Binary file not shown.

View file

@ -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());
}

BIN
hello Executable file

Binary file not shown.

24
hello.asm Normal file
View file

@ -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

BIN
hello.o Normal file

Binary file not shown.

BIN
hello.so Executable file

Binary file not shown.

BIN
lib Executable file

Binary file not shown.

32
out.asm Normal file
View file

@ -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

395
out2.asm Normal file
View file

@ -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

BIN
out2.o Normal file

Binary file not shown.

BIN
rust Executable file

Binary file not shown.

13
rust.rs Normal file
View file

@ -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();
}

315
rust.s Normal file
View file

@ -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