From 984f0c0d99a2cb5432801ff6cfa3f976e61139d4 Mon Sep 17 00:00:00 2001 From: Giordano Salvador <73959795+e3m3@users.noreply.github.com> Date: Fri, 20 Sep 2024 01:49:13 -0400 Subject: [PATCH] Enable object file and executable output * Added C-derived main code generation, using clang * Use llc and clang for object files and executables * Added cli shorthand flags for convenience * Removed lit crate dependency and tests * Refactored code and tests * Version bump --- .gitignore | 4 +- Cargo.toml | 3 +- README.md | 23 +- src/command.rs | 91 +++++ src/exit_code.rs | 12 +- src/irgen.rs | 15 +- src/lex.rs | 16 +- src/main.c.template | 41 +++ src/main.rs | 310 ++++++++++++------ src/maingen.rs | 20 +- src/maingen_c.rs | 146 +++++++++ src/module.rs | 202 +++++++++++- src/options.rs | 187 ++++++++++- src/target.rs | 49 ++- tests/lit-llvm/codegen_type.calc | 14 - tests/lit-llvm/end_to_end1.calc | 9 - tests/lit-llvm/end_to_end2.calc | 49 --- tests/lit-llvm/lex_other.calc | 19 -- tests/lit-llvm/parse_eoi.calc | 21 -- tests/lit-llvm/parse_idents.calc | 20 -- tests/lit-llvm/parse_numbers.calc | 31 -- tests/lit-rust/lex_hello.calc | 14 - tests/lit-rust/lex_number.calc | 9 - tests/lit-rust/usage.calc | 4 - tests/lit-rust/version.calc | 5 - tests/lit-tests-rust.rs | 39 --- tests/{lit-tests-llvm.rs => lit-tests.rs} | 2 +- tests/lit-tests/codegen_type.calc | 28 ++ tests/lit-tests/codegen_type_c.calc | 26 ++ tests/lit-tests/end_to_end1.calc | 8 + tests/lit-tests/end_to_end2.calc | 49 +++ tests/lit-tests/end_to_end3.calc | 8 + tests/lit-tests/end_to_end4.calc | 49 +++ tests/lit-tests/end_to_end_c1.calc | 7 + tests/lit-tests/end_to_end_c2.calc | 47 +++ .../{lit-llvm => lit-tests}/input_file1.calc | 7 +- tests/lit-tests/input_file_c1.calc | 9 + tests/{lit-llvm => lit-tests}/irgen1.calc | 6 +- tests/{lit-llvm => lit-tests}/irgen2.calc | 6 +- .../{lit-llvm => lit-tests}/lex_comment.calc | 6 +- tests/{lit-llvm => lit-tests}/lex_drop.calc | 4 +- tests/{lit-llvm => lit-tests}/lex_hello.calc | 8 +- tests/{lit-llvm => lit-tests}/lex_idents.calc | 14 +- tests/{lit-llvm => lit-tests}/lex_nodrop.calc | 4 +- tests/{lit-llvm => lit-tests}/lex_number.calc | 4 +- .../lex_number_enhanced.calc | 14 +- tests/{lit-rust => lit-tests}/lex_other.calc | 2 +- tests/{lit-llvm => lit-tests}/lex_stdin.calc | 2 +- .../lex_trailing_whitespace.calc | 4 +- .../{lit-llvm => lit-tests}/lex_withdecl.calc | 6 +- tests/{lit-llvm => lit-tests}/lit.cfg | 10 + tests/{lit-llvm => lit-tests}/maingen1.calc | 4 +- tests/{lit-llvm => lit-tests}/maingen2.calc | 4 +- tests/lit-tests/option_c_main.calc | 60 ++++ .../option_notarget.calc | 8 +- tests/{lit-llvm => lit-tests}/option_opt.calc | 9 +- tests/lit-tests/parse_eoi.calc | 21 ++ .../parse_expr_arith.calc | 12 +- .../parse_expr_unary_minus.calc | 14 +- .../parse_expr_withdecl.calc | 4 +- tests/lit-tests/parse_idents.calc | 20 ++ tests/lit-tests/parse_numbers.calc | 31 ++ tests/{lit-llvm => lit-tests}/sem_decl.calc | 10 +- tests/{lit-llvm => lit-tests}/usage.calc | 0 tests/{lit-llvm => lit-tests}/version.calc | 2 +- 65 files changed, 1394 insertions(+), 488 deletions(-) create mode 100644 src/command.rs create mode 100644 src/main.c.template create mode 100644 src/maingen_c.rs delete mode 100644 tests/lit-llvm/codegen_type.calc delete mode 100644 tests/lit-llvm/end_to_end1.calc delete mode 100644 tests/lit-llvm/end_to_end2.calc delete mode 100644 tests/lit-llvm/lex_other.calc delete mode 100644 tests/lit-llvm/parse_eoi.calc delete mode 100644 tests/lit-llvm/parse_idents.calc delete mode 100644 tests/lit-llvm/parse_numbers.calc delete mode 100644 tests/lit-rust/lex_hello.calc delete mode 100644 tests/lit-rust/lex_number.calc delete mode 100644 tests/lit-rust/usage.calc delete mode 100644 tests/lit-rust/version.calc delete mode 100644 tests/lit-tests-rust.rs rename tests/{lit-tests-llvm.rs => lit-tests.rs} (98%) create mode 100644 tests/lit-tests/codegen_type.calc create mode 100644 tests/lit-tests/codegen_type_c.calc create mode 100644 tests/lit-tests/end_to_end1.calc create mode 100644 tests/lit-tests/end_to_end2.calc create mode 100644 tests/lit-tests/end_to_end3.calc create mode 100644 tests/lit-tests/end_to_end4.calc create mode 100644 tests/lit-tests/end_to_end_c1.calc create mode 100644 tests/lit-tests/end_to_end_c2.calc rename tests/{lit-llvm => lit-tests}/input_file1.calc (85%) create mode 100644 tests/lit-tests/input_file_c1.calc rename tests/{lit-llvm => lit-tests}/irgen1.calc (73%) rename tests/{lit-llvm => lit-tests}/irgen2.calc (62%) rename tests/{lit-llvm => lit-tests}/lex_comment.calc (88%) rename tests/{lit-llvm => lit-tests}/lex_drop.calc (81%) rename tests/{lit-llvm => lit-tests}/lex_hello.calc (57%) rename tests/{lit-llvm => lit-tests}/lex_idents.calc (75%) rename tests/{lit-llvm => lit-tests}/lex_nodrop.calc (81%) rename tests/{lit-llvm => lit-tests}/lex_number.calc (79%) rename tests/{lit-llvm => lit-tests}/lex_number_enhanced.calc (77%) rename tests/{lit-rust => lit-tests}/lex_other.calc (90%) rename tests/{lit-llvm => lit-tests}/lex_stdin.calc (82%) rename tests/{lit-llvm => lit-tests}/lex_trailing_whitespace.calc (90%) rename tests/{lit-llvm => lit-tests}/lex_withdecl.calc (91%) rename tests/{lit-llvm => lit-tests}/lit.cfg (86%) rename tests/{lit-llvm => lit-tests}/maingen1.calc (98%) rename tests/{lit-llvm => lit-tests}/maingen2.calc (97%) create mode 100644 tests/lit-tests/option_c_main.calc rename tests/{lit-llvm => lit-tests}/option_notarget.calc (69%) rename tests/{lit-llvm => lit-tests}/option_opt.calc (60%) create mode 100644 tests/lit-tests/parse_eoi.calc rename tests/{lit-llvm => lit-tests}/parse_expr_arith.calc (76%) rename tests/{lit-llvm => lit-tests}/parse_expr_unary_minus.calc (76%) rename tests/{lit-llvm => lit-tests}/parse_expr_withdecl.calc (89%) create mode 100644 tests/lit-tests/parse_idents.calc create mode 100644 tests/lit-tests/parse_numbers.calc rename tests/{lit-llvm => lit-tests}/sem_decl.calc (56%) rename tests/{lit-llvm => lit-tests}/usage.calc (100%) rename tests/{lit-llvm => lit-tests}/version.calc (75%) diff --git a/.gitignore b/.gitignore index 209a7a4..2114cbd 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,5 @@ Cargo.lock *.bak -tests/lit-llvm/.lit_test_times.txt -tests/lit-llvm/Output +tests/lit-tests/.lit_test_times.txt +tests/lit-tests/Output diff --git a/Cargo.toml b/Cargo.toml index 1725c03..8de7ebd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "calcc" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,7 +9,6 @@ edition = "2021" llvm-sys = { version = "=181.1.1", features = ["force-dynamic"] } [dev-dependencies] -lit = "1.0.4" [lints.clippy] unused_unit = "allow" diff --git a/README.md b/README.md index 7241248..9857fce 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Learning [Rust][1] [[1]] by implementing the calc langauge using the [llvm-sys][ Implements the calc language, inspired by the [C++][3] [[3]] implementation presented by Macke and Kwan in [[4]] and [[5]]. Accepted factors in the grammar have been extended for convenience (see `src/{lex,parse}.rs` and `tests/lit-llvm/`). -The output of the compiler is LLVM IR or LLVM bytecode [[6]]. +The output of the compiler is LLVM IR, LLVM bitcode, an object file, or executable file [[6]]. ## Language @@ -95,7 +95,9 @@ Notes: * llvm18 and llvm-sys (or llvm version matching llvm-sys) -* python3-lit, FileCheck, clang (for testing) +* clang-18 (for executables and '-C|--c-main' flags) + +* python3-lit, FileCheck (for testing) * By default, `tests/lit-tests-llvm.rs` will search for the lit executable in the `$PYTHON_VENV_PATH/bin` (if it exists) or the system's `/usr/bin`. @@ -143,17 +145,26 @@ usage: calcc [OPTIONS] INPUT '-' (i.e., Stdin) or a file path OPTIONS: --ast Print the AST after parsing +-b|--bitcode Output LLVM bitcode (post-optimization) (.bc if used with -o) +-c Output an object file (post-optimization) (.o if used with -o) --drop Drop unknown tokens instead of failing -e|--expr[=] Process expression E instead of INPUT file -h|--help Print this list of command line options --lex Exit after running the lexer --S|--llvmir Exit after outputting LLVM IR (post-optimization) instead of byte code ---nomain Omit linking with main module (i.e., output kernel only) ---notarget Omit target specific configuration in LLVM IR/bytecode --o[=] Output to LLVM IR (.ll) or bytecode (.bc) file F instead of Stdout +--ir Exit after printing IR (pre-optimization) +-S|--llvmir Output LLVM IR (post-optimization) (.ll if used with -o) +-k|--no-main Omit linking with main module (i.e., output kernel only) + When this option is selected, an executable cannot be generated +--notarget Omit target specific configuration in LLVM IR/bitcode +-o[=] Output to file F instead of Stdout + If no known extension is used (.bc|.exe|.ll|.o) an executable is assumed + An executable requires llc and clang to be installed -O<0|1|2|3> Set the optimization level (default: O2) --parse Exit after running the parser --sem Exit after running the semantics check +-C|--c-main Link with a C-derived main module (src/main.c.template) + This option is required for generating object files and executables on MacOS + and requires clang to be installed -v|--verbose Enable verbose output --version Display the package version and license information ``` diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..c0dbc0a --- /dev/null +++ b/src/command.rs @@ -0,0 +1,91 @@ +// Copyright 2024, Giordano Salvador +// SPDX-License-Identifier: BSD-3-Clause + +use crate::exit_code; +use exit_code::exit; +use exit_code::ExitCode; + +use std::io::Write; +use std::process; +use std::process::Stdio; + +pub struct CommandResult { + pub success: bool, + pub stdout: Option, +} + +impl CommandResult { + pub fn new(success: bool, stdout: Option) -> Self { + CommandResult{success, stdout} + } +} + +pub struct Command {} + +impl Command { + pub fn run(bin: &str, args: &[&str]) -> CommandResult { + let output = match process::Command::new(bin).args(args.iter()).output() { + Ok(output) => output, + Err(msg) => { + eprintln!("Failed to run with {}: {}", bin, msg); + exit(ExitCode::CommandError); + }, + }; + let stderr: &[u8] = output.stderr.as_slice(); + let stdout: &[u8] = output.stdout.as_slice(); + if !stderr.is_empty() { + eprintln!("{} stderr:\n{}", bin, std::str::from_utf8(stderr).unwrap()); + } + let stdout_opt = if !stdout.is_empty() { + Some(std::str::from_utf8(stdout).unwrap().to_string()) + } else { + None + }; + CommandResult::new(output.status.success(), stdout_opt) + } + + pub fn run_with_input(bin: &str, args: &[&str], input: &str) -> CommandResult { + let mut proc = match process::Command::new(bin) + .args(args.iter()) + .stdin(Stdio::piped()) + .spawn() { + Ok(proc) => proc, + Err(msg) => { + eprintln!("Failed to spawn {} child process: {}", bin, msg); + exit(ExitCode::CommandError); + }, + }; + let proc_stdin = match proc.stdin.as_mut() { + Some(stdin) => stdin, + None => { + eprintln!("Failed to get {} process stdin handle", bin); + exit(ExitCode::CommandError); + }, + }; + match proc_stdin.write_all(input.as_bytes()) { + Ok(()) => (), + Err(msg) => { + eprintln!("Failed to write input to {} process stdin: {}", bin, msg); + exit(ExitCode::CommandError); + }, + }; + let output = match proc.wait_with_output() { + Ok(output) => output, + Err(msg) => { + eprintln!("Failed to run with {}: {}", bin, msg); + exit(ExitCode::CommandError); + }, + }; + let stderr: &[u8] = output.stderr.as_slice(); + let stdout: &[u8] = output.stdout.as_slice(); + if !stderr.is_empty() { + eprintln!("{} stderr:\n{}", bin, std::str::from_utf8(stderr).unwrap()); + } + let stdout_opt = if !stdout.is_empty() { + Some(std::str::from_utf8(stdout).unwrap().to_string()) + } else { + None + }; + CommandResult::new(output.status.success(), stdout_opt) + } +} diff --git a/src/exit_code.rs b/src/exit_code.rs index e718a77..88a0a69 100644 --- a/src/exit_code.rs +++ b/src/exit_code.rs @@ -13,11 +13,13 @@ pub enum ExitCode { SemanticError = 4, ModuleError = 5, IRGenError = 6, - _MainGenError = 7, - VerifyError = 8, - TargetError = 9, - LinkError = 10, - WriteError = 11, + MainGenError = 7, + MainGenCError = 8, + VerifyError = 9, + TargetError = 10, + LinkError = 11, + WriteError = 12, + CommandError = 13, } pub fn exit(code: ExitCode) -> ! { diff --git a/src/irgen.rs b/src/irgen.rs index 70c7dc9..60b42be 100644 --- a/src/irgen.rs +++ b/src/irgen.rs @@ -3,7 +3,18 @@ extern crate llvm_sys as llvm; -use llvm::core::*; +use llvm::core::LLVMAddFunction; +use llvm::core::LLVMAppendBasicBlockInContext; +use llvm::core::LLVMBuildLoad2; +use llvm::core::LLVMBuildNSWAdd; +use llvm::core::LLVMBuildNSWMul; +use llvm::core::LLVMBuildNSWSub; +use llvm::core::LLVMBuildRet; +use llvm::core::LLVMBuildSDiv; +use llvm::core::LLVMBuildStore; +use llvm::core::LLVMFunctionType; +use llvm::core::LLVMGetParam; +use llvm::core::LLVMPositionBuilderAtEnd; use llvm::prelude::LLVMBasicBlockRef; use llvm::prelude::LLVMBool; use llvm::prelude::LLVMTypeRef; @@ -34,7 +45,7 @@ pub struct IRGen<'a, 'b> { } impl <'a, 'b> IRGen<'a, 'b> { - pub fn new(bundle: &'a mut ModuleBundle<'b>) -> Self { + fn new(bundle: &'a mut ModuleBundle<'b>) -> Self { IRGen{bundle} } diff --git a/src/lex.rs b/src/lex.rs index c6c7aa2..68ce8ff 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -35,7 +35,7 @@ pub enum TokenKind { } pub fn token_kind_to_string(k: TokenKind) -> String { - String::from(match k { + match k { TokenKind::Comma => "Comma", TokenKind::Comment => "Comment", TokenKind::Colon => "Colon", @@ -51,7 +51,7 @@ pub fn token_kind_to_string(k: TokenKind) -> String { TokenKind::Star => "Star", TokenKind::Unknown => "Unknown", TokenKind::With => "With", - }) + }.to_string() } #[derive(Clone)] @@ -273,17 +273,7 @@ impl <'a, T: Read> Lexer<'a, T> { } fn is_other(c: char) -> bool { - match c { - ',' => true, - ':' => true, - '-' => true, - '(' => true, - ')' => true, - '+' => true, - '/' => true, - '*' => true, - _ => false, - } + matches!(c, ',' | ':' | '-' | '(' | ')' | '+' | '/' | '*') } fn is_whitespace(c: char) -> bool { diff --git a/src/main.c.template b/src/main.c.template new file mode 100644 index 0000000..21251f2 --- /dev/null +++ b/src/main.c.template @@ -0,0 +1,41 @@ +// Copyright 2024, Giordano Salvador +// SPDX-License-Identifier: BSD-3-Clause + +/// Description: A C-stub used to generate the main module for linking during compile time. +/// Values prefixed by `@@` are expected inputs from the compiler for text substitution. +/// Inputs: +/// * NUM_ARGS : a `usize` for the length of the parameters list +/// * USAGE_ARGS : a comma separated list of args for the usage prompt +/// : (e.g., , , ... +/// * PARAM_TYPES_LIST : a comma separated list of types for the callee prototype +/// * PARAM_DECLS_LIST : a sequence of statements assigning temporaries for the callee +/// (e.g., const t_i64 p0 = (t_i64)atoll(argv[BASE + 0]); ... ) +/// * PARAMS_LIST : a comma separated list of uses of the temporaries for the callee +/// (e.g., p0, p1, ... ) + +#include +#include + +#define BASE 1 +#define NUM_ARGS @@NUM_ARGS +#define USAGE " @@USAGE_ARGS\n" + +typedef long long t_i64; +extern t_i64 calcc_main(@@PARAM_TYPES_LIST); + +int main(int argc, char **argv) { + if (argc != BASE + NUM_ARGS) { + (void) fprintf(stderr, "Invalid number of args to main. Expected %d args\n", NUM_ARGS); + (void) fprintf(stderr, USAGE); + return 1; + } + + /* Parameter declaration section: */ + @@PARAM_DECLS_LIST + + /* Function call section: */ + const t_i64 result = calcc_main(@@PARAMS_LIST); + (void) printf("calcc_main result: %lld\n", result); + + return 0; +} diff --git a/src/main.rs b/src/main.rs index d1cdb00..99187d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,19 +5,23 @@ const LICENSE: &str = "\ // SPDX-License-Identifier: BSD-3-Clause\n\ "; const PACKAGE: &str = "calcc"; -const VERSION: &str = "v0.1.0-dev"; +const VERSION: &str = "v0.2.0"; use std::env; +use std::fmt; use std::fs::File; use std::io::Cursor; use std::io::stdin; -use std::io::Write; +use std::path::Path; +use std::process; mod ast; +mod command; mod exit_code; mod irgen; mod lex; mod maingen; +mod maingen_c; mod module; mod parse; mod options; @@ -32,12 +36,17 @@ use irgen::IRGen; use lex::Lexer; use lex::Token; use maingen::MainGen; +use maingen_c::MainGenC; use module::ModuleBundle; -use options::OptLevel; +use options::BodyType; use options::CodeGenType; +use options::HostOS; +use options::OptLevel; +use options::OutputType; use options::RunOptions; use parse::Parser; use sem::Semantics; +use target::Passes; use target::PassBuilder; use target::Target; use target::TargetMachine; @@ -47,17 +56,26 @@ fn help(code: ExitCode) -> ! { "INPUT '-' (i.e., Stdin) or a file path", "OPTIONS:", "--ast Print the AST after parsing", + "-b|--bitcode Output LLVM bitcode (post-optimization) (.bc if used with -o)", + "-c Output an object file (post-optimization) (.o if used with -o)", "--drop Drop unknown tokens instead of failing", "-e|--expr[=] Process expression E instead of INPUT file", "-h|--help Print this list of command line options", "--lex Exit after running the lexer", - "-S|--llvmir Exit after outputting LLVM IR (post-optimization) instead of byte code", - "--nomain Omit linking with main module (i.e., output kernel only)", - "--notarget Omit target specific configuration in LLVM IR/bytecode", - "-o[=] Output to LLVM IR (.ll) or bytecode (.bc) file F instead of Stdout", + "--ir Exit after printing IR (pre-optimization)", + "-S|--llvmir Output LLVM IR (post-optimization) (.ll if used with -o)", + "-k|--no-main Omit linking with main module (i.e., output kernel only)", + " When this option is selected, an executable cannot be generated", + "--notarget Omit target specific configuration in LLVM IR/bitcode", + "-o[=] Output to file F instead of Stdout", + " If no known extension is used (.bc|.exe|.ll|.o) an executable is assumed", + " An executable requires llc and clang to be installed", "-O<0|1|2|3> Set the optimization level (default: O2)", "--parse Exit after running the parser", "--sem Exit after running the semantics check", + "-C|--c-main Link with a C-derived main module (src/main.c.template)", + " This option is required for generating object files and executables on MacOS", + " and requires clang to be installed", "-v|--verbose Enable verbose output", "--version Display the package version and license information", ].join("\n")); @@ -72,25 +90,98 @@ enum InputType<'a> { File(&'a str), } -fn input_type_to_string(t: InputType) -> String { - match t { - InputType::Stdin => "Stdin".to_string(), - InputType::Expr(e) => format!("Expression:{}", e), - InputType::File(f) => format!("File:{}", f), - InputType::None => "None".to_string(), +impl <'a> fmt::Display for InputType<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + InputType::Stdin => "Stdin".to_string(), + InputType::Expr(e) => format!("Expression:{}", e), + InputType::File(f) => format!("File:{}", f), + InputType::None => "None".to_string(), + }; + write!(f, "{}", s) + } +} + +fn set_codegen_type(options: &mut RunOptions, codegen_type: CodeGenType) -> () { + if options.codegen_type == CodeGenType::Unset { + options.codegen_type = codegen_type; + } else { + eprintln!("Incompatible compiler flags for output type: {} and {}", options.codegen_type, codegen_type); + exit(ExitCode::ArgParseError); + } +} + +fn set_body_type(options: &mut RunOptions, body_type: BodyType) -> () { + if options.body_type == BodyType::Unset { + options.body_type = body_type; + } else { + eprintln!("Incompatible compiler flags: '-k|--no-main' and '-C|--c-main'"); + exit(ExitCode::ArgParseError); } } #[derive(Clone,Copy)] -enum OutputType<'a> { - Stdout, - File(&'a str), +enum ExtType { + None, + BC, + Exe, + LL, + O, } -fn output_type_to_string(t: OutputType) -> String { - match t { - OutputType::Stdout => "Stdout".to_string(), - OutputType::File(f) => format!("File:{}", f), +fn get_extension_from_filename(name: &str) -> ExtType { + match Path::new(name).extension() { + None => ExtType::None, + Some(ext) => match ext.to_str().unwrap() { + "bc" => ExtType::BC, + "exe" => ExtType::Exe, + "ll" => ExtType::LL, + "o" => ExtType::O, + _ => ExtType::None, + }, + } +} + +/// Checks to ensure valid combination for BodyType, CodeGenType, and OutputType +fn check_options_configuration(options: &RunOptions, output: &OutputType) -> () { + match *output { + OutputType::Stdout => (), + OutputType::File(f) => { + let t = options.codegen_type; + match get_extension_from_filename(f) { + ExtType::None => if t != CodeGenType::Executable { + eprintln!("Output name (no/unknown extension) should match codegen type: {} specified", t); + exit(ExitCode::ArgParseError); + }, + ExtType::BC => if t != CodeGenType::Bitcode { + eprintln!("Output name ('.bc' extension) should match codegen type (-b|--bitcode)"); + exit(ExitCode::ArgParseError); + }, + ExtType::Exe => if t != CodeGenType::Executable { + eprintln!("Output name ('.exe' extension) should match codegen type: {} specified", t); + exit(ExitCode::ArgParseError); + }, + ExtType::LL => if t != CodeGenType::Llvmir { + eprintln!("Output name ('.ll' extension) should match codegen type (-S|--llvmir)"); + exit(ExitCode::ArgParseError); + }, + ExtType::O => if t != CodeGenType::Object { + eprintln!("Output name ('.o' extension) should match codegen type (-c)"); + exit(ExitCode::ArgParseError); + }, + } + }, + }; + + if options.body_type == BodyType::NoMain && options.codegen_type == CodeGenType::Executable { + eprintln!("Unsupported -k-|-no-main with executable output type"); + exit(ExitCode::ArgParseError); + } + + if options.host_os == HostOS::MacOS && options.body_type == BodyType::MainGen { + eprintln!("Linking the C standard library from a kernel+main module is not supported on MacOS"); + eprintln!("Please use the C-derived main option (-C|--c-main)"); + exit(ExitCode::ArgParseError); } } @@ -108,14 +199,20 @@ fn parse_args<'a>( arg = args.get(i).unwrap(); match arg.as_str() { "--ast" => options.print_ast = true, + "-b" => set_codegen_type(options, CodeGenType::Bitcode), + "--bitcode" => set_codegen_type(options, CodeGenType::Bitcode), + "-c" => set_codegen_type(options, CodeGenType::Object), + "-C" => set_body_type(options, BodyType::MainGenC), "--drop" => options.drop_token = true, "-e" => *input = InputType::Expr(parse_arg_after(args, &mut i)), "--expr" => *input = InputType::Expr(parse_arg_after(args, &mut i)), "-h" => help(ExitCode::Ok), "--help" => help(ExitCode::Ok), + "--ir" => options.ir_exit = true, + "-k" => set_body_type(options, BodyType::NoMain), "--lex" => options.lex_exit = true, - "--llvmir" => options.codegen_type = CodeGenType::Llvmir, - "--nomain" => options.no_main = true, + "--llvmir" => set_codegen_type(options, CodeGenType::Llvmir), + "--no-main" => set_body_type(options, BodyType::NoMain), "--notarget" => options.no_target = true, "-o" => *output = OutputType::File(parse_arg_after(args, &mut i)), "-O0" => options.opt_level = OptLevel::O0, @@ -124,7 +221,8 @@ fn parse_args<'a>( "-O3" => options.opt_level = OptLevel::O3, "--parse" => options.parse_exit = true, "--sem" => options.sem_exit = true, - "-S" => options.codegen_type = CodeGenType::Llvmir, + "-S" => set_codegen_type(options, CodeGenType::Llvmir), + "--c-main" => set_body_type(options, BodyType::MainGenC), "-v" => options.verbose = true, "--verbose" => options.verbose = true, "--version" => print_pkg_info(true), @@ -133,37 +231,28 @@ fn parse_args<'a>( i += 1; } + if options.body_type == BodyType::Unset { + set_body_type(options, BodyType::MainGen); + } + + if options.codegen_type == CodeGenType::Unset { + set_codegen_type(options, CodeGenType::Executable); + } + if *input == InputType::None { eprintln!("No input file/name specified!"); help(ExitCode::ArgParseError); } else if options.verbose { - eprintln!("Processing input '{}'", input_type_to_string(*input)); + eprintln!("Processing input '{}'", *input); } - match *output { - OutputType::Stdout => (), - OutputType::File(f) => { - let len = f.len(); - if len < 3 { - eprintln!("Malformed output name '{}'", f); - exit(ExitCode::ArgParseError); - } - let ext: &str = &f[len-3..]; - if ext != ".bc" && ext != ".ll" { - eprintln!("Output name '{}' should end in '.bc' or '.ll", f); - exit(ExitCode::ArgParseError); - } else if ext == ".bc" && options.codegen_type == CodeGenType::Llvmir { - eprintln!("LLVM IR file type (with '-S' flag) should match output name ('.ll' extension)"); - exit(ExitCode::ArgParseError); - } else if ext == ".ll" && options.codegen_type == CodeGenType::Bytecode { - eprintln!("Bytecode file type (missing '-S' flag) should match output name ('.bc' extension)"); - exit(ExitCode::ArgParseError); - } - }, - }; + if options.verbose { + eprintln!("Outputting to '{}'", *output); + } + check_options_configuration(options, output); if options.verbose { - eprintln!("Outputting to '{}'", output_type_to_string(*output)); + eprintln!("{}", options); } } @@ -206,7 +295,7 @@ fn parse_arg_complex<'a>( } } } else if *input != InputType::None { - eprintln!("Found more than one input ('{}' and '{}')", input_type_to_string(*input), arg); + eprintln!("Found more than one input ('{}' and '{}')", *input, arg); help(ExitCode::ArgParseError); } else if arg.len() == 1 && lead_char == '-' { *input = InputType::Stdin; @@ -220,47 +309,6 @@ fn print_pkg_info(should_exit: bool) { if should_exit { exit(ExitCode::Ok); } } -fn write_module(bundle: &mut ModuleBundle, codegen_type: CodeGenType, output: &OutputType) -> bool { - match *output { - OutputType::Stdout => { - match codegen_type { - CodeGenType::Llvmir => { - let string: String = bundle.to_string(); - println!("{}", string); - }, - CodeGenType::Bytecode => { - eprintln!("Unimplemented: writing bytecode to Stdout"); - return false; - }, - } - }, - OutputType::File(f) => { - match codegen_type { - CodeGenType::Llvmir => { - let string: String = bundle.to_string(); - let mut file = match File::create(f) { - Ok(file) => file, - Err(msg) => { - eprintln!("Failed to open output file '{}': {}", f, msg); - return false; - } - }; - match file.write_all(string.as_bytes()) { - Ok(()) => (), - Err(msg) => { - eprintln!("Failed to write to output file '{}': {}", f, msg); - return false; - } - } - }, - CodeGenType::Bytecode => bundle.write_bitcode_to_file(f), - } - }, - } - - true -} - fn main() -> ! { let args: Vec = env::args().collect(); let mut input: InputType = InputType::None; @@ -309,40 +357,88 @@ fn main() -> ! { } let module_name_main = String::from("main"); - let mut module_main = if options.no_main { - module_irgen - } else { - let mut module_main = ModuleBundle::new(&module_name_main, options.verbose); + let mut module_main = match options.body_type { + BodyType::NoMain => module_irgen, + BodyType::MainGen => { + let mut module_main = ModuleBundle::new(&module_name_main, options.verbose); - // NOTE: Fixing the verifier errors by reconstructing the callee signature using the correct main module - // context causes the link step to omit the body of the callee (emitting only a declaration). - //let f_sig = module_main.get_f_sig_from_context(&module_irgen.f_sig.clone().unwrap()); - let f_sig = &module_irgen.f_sig.clone().unwrap(); + // NOTE: Fixing the verifier errors by reconstructing the callee signature using + // the correct main module context causes the link step to omit the body of the callee + // (emitting only a declaration). + //let f_sig = module_main.get_f_sig_from_context(&module_irgen.f_sig.clone().unwrap()); + let f_sig = &module_irgen.f_sig.clone().unwrap(); - let maingen_status: bool = MainGen::gen(&mut module_main, f_sig); - assert!(maingen_status); + let maingen_status: bool = MainGen::gen(&mut module_main, f_sig); + if !maingen_status { + eprintln!("Failed to generate MainGen module"); + exit(ExitCode::MainGenError); + } - // NOTE: Linking modules causes verifier errors for unmatched contexts of LLVMTypeRef. Skip verification. - let link_status: bool = module_main.link_into(&mut module_irgen); - if !link_status { - eprintln!("IRGen and MainGen modules failed to link"); - exit(ExitCode::LinkError); - } - module_main + // NOTE: Linking modules causes verifier errors for unmatched contexts of LLVMTypeRef. + // Skip verification. + let link_status: bool = module_main.link_into(&mut module_irgen); + if !link_status { + eprintln!("IRGen and MainGen modules failed to link"); + exit(ExitCode::LinkError); + } + + module_main + }, + BodyType::MainGenC => { + // Generating the main.c object need to happen until after the target/optimization step, + // but to appease the borrow checker, we do it here. + let f_sig = &module_irgen.f_sig.clone().unwrap(); + let stem = format!("{}pid{}", + match input { + InputType::File(f) => Path::new(f).file_stem().unwrap().to_str().unwrap(), + _ => "", + }, + process::id() + ); + let main_obj_path = match MainGenC::gen(f_sig, &stem, options.verbose) { + Some(path) => path, + None => { + eprintln!("Failed to generate MainGenC object file"); + exit(ExitCode::MainGenCError); + }, + }; + + module_irgen.push_object(main_obj_path); + module_irgen + }, + BodyType::Unset => { + eprintln!("Invalid body type"); + exit(ExitCode::ModuleError); + }, }; + match input { + InputType::File(f) => module_main.set_sourcefile_name(f), + _ => module_main.set_sourcefile_name("-"), + }; + + if options.ir_exit { + eprintln!("{}", module_main); + exit(ExitCode::Ok); + } + let mut target = Target::new(); if options.verbose { eprintln!("Detected target triple '{}'", target.get_string()); } let mut machine = TargetMachine::new(&mut target, options.opt_level); let mut pass_builder = PassBuilder::new(); - let opt_result = pass_builder.run(&mut module_main, &mut machine, options.opt_level, options.no_target); + let opt_result = pass_builder.run( + &mut module_main, + &mut machine, + Passes::Default(options.opt_level), + options.no_target + ); if !opt_result { exit(ExitCode::TargetError); } - let write_result: bool = write_module(&mut module_main, options.codegen_type, &output); + let write_result = module_main.write_module(&options, &output); if !write_result { eprintln!("Failed to write module to output"); exit(ExitCode::WriteError); diff --git a/src/maingen.rs b/src/maingen.rs index 36ea934..9aa094a 100644 --- a/src/maingen.rs +++ b/src/maingen.rs @@ -5,11 +5,25 @@ /// program function generated for the input program. /// This approach is used to avoid interfacing with a stub program written in C, directly calling /// the dependent library functions. -/// Omit this code generation by passing '--nomain' to calcc. +/// Omit this code generation by passing '--no-main' to calcc. extern crate llvm_sys as llvm; -use llvm::core::*; +use llvm::core::LLVMAddFunction; +use llvm::core::LLVMAddGlobal; +use llvm::core::LLVMAppendBasicBlockInContext; +use llvm::core::LLVMBuildBr; +use llvm::core::LLVMBuildCall2; +use llvm::core::LLVMBuildCondBr; +use llvm::core::LLVMBuildGEP2; +use llvm::core::LLVMBuildICmp; +use llvm::core::LLVMBuildLoad2; +use llvm::core::LLVMBuildRet; +use llvm::core::LLVMBuildStore; +use llvm::core::LLVMFunctionType; +use llvm::core::LLVMGetParam; +use llvm::core::LLVMPositionBuilderAtEnd; +use llvm::core::LLVMSetAlignment; use llvm::prelude::LLVMBasicBlockRef; use llvm::prelude::LLVMBool; use llvm::prelude::LLVMTypeRef; @@ -46,7 +60,7 @@ pub struct MainGen<'a, 'b> { } impl <'a, 'b> MainGen<'a, 'b> { - pub fn new(bundle: &'a mut ModuleBundle<'b>) -> Self { + fn new(bundle: &'a mut ModuleBundle<'b>) -> Self { MainGen{bundle} } diff --git a/src/maingen_c.rs b/src/maingen_c.rs new file mode 100644 index 0000000..82524b3 --- /dev/null +++ b/src/maingen_c.rs @@ -0,0 +1,146 @@ +// Copyright 2024, Giordano Salvador +// SPDX-License-Identifier: BSD-3-Clause + +/// Generate LLVM IR for a main function, using the C standard library, from `main.c.template`. +/// Enable this using `--c-main`/`-C`. + +extern crate llvm_sys as llvm; +use llvm::prelude::LLVMTypeRef; + +use std::env; +use std::path::Path; +use std::str; + +use crate::command; +use crate::module; +use crate::exit_code; + +use command::Command; +use exit_code::exit; +use exit_code::ExitCode; +use module::FunctionSignature; +use module::ModuleBundle; + +pub struct MainGenC {} + +const INPUT_NUM_ARGS : &str = "@@NUM_ARGS"; +const INPUT_USAGE_ARGS : &str = "@@USAGE_ARGS"; +const INPUT_PARAM_TYPES_LIST: &str = "@@PARAM_TYPES_LIST"; +const INPUT_PARAM_DECLS_LIST: &str = "@@PARAM_DECLS_LIST"; +const INPUT_PARAMS_LIST : &str = "@@PARAMS_LIST"; +const MAIN_C_TEMPLATE : &str = include_str!("main.c.template"); + +const CLANG_AGS: [&str; 9] = [ + "-x", "c", "-", "-Wall", "-Werror", "-std=c99", "-fPIC", "-c", "-o" +]; + +impl MainGenC { + /// Returns a path (option) to the object file generated from `main.c.tempalte`. + /// This object file will be linked to the object file generated from the LLVM during the + /// IRGen stage. + pub fn gen(callee_sig: &FunctionSignature, stem: &str, verbose: bool) -> Option { + let body = Self::substitute_all_inputs_in_body(MAIN_C_TEMPLATE, callee_sig, verbose); + let mut clang_args: Vec<&str> = Vec::from(CLANG_AGS); + let dir = env::temp_dir(); + let main_obj_path = Self::get_main_obj_path(&dir, stem); + if verbose { + eprintln!("Path to main object file: {}", main_obj_path); + } + clang_args.push(main_obj_path.as_str()); + let clang_result = Command::run_with_input("clang", &clang_args, &body); + if clang_result.stdout.is_some() { + println!("{}", clang_result.stdout.unwrap()); + } + if clang_result.success { + Some(main_obj_path) + } else { + None + } + } + + fn get_main_obj_path(dir: &Path, stem: &str) -> String { + match dir.join(format!("main.{}.o", stem)).as_path().to_str() { + Some(path) => path, + None => { + eprintln!("Failed to create path for main.o"); + exit(ExitCode::MainGenCError); + }, + }.to_string() + } + + fn substitute_all_inputs_in_body(body: &str, callee_sig: &FunctionSignature, verbose: bool) -> String { + let num_args = format!("{}", callee_sig.params.len()); + let usage_string = Self::get_usage_args_string(&callee_sig.params); + let param_decls = Self::collect_callee_param_decls_string(&callee_sig.params, 1); + let param_types_list = Self::get_callee_param_types_list_string(&callee_sig.params); + let params_list = Self::get_callee_params_list_string(&callee_sig.params); + let body_with_num_args = Self::substitute_param_in_string(body, INPUT_NUM_ARGS, &num_args); + let body_with_usage_args = Self::substitute_param_in_string( + &body_with_num_args, INPUT_USAGE_ARGS, &usage_string + ); + let body_with_param_decls = Self::substitute_param_in_string( + &body_with_usage_args, INPUT_PARAM_DECLS_LIST, ¶m_decls + ); + let body_with_param_types_list = Self::substitute_param_in_string( + &body_with_param_decls, INPUT_PARAM_TYPES_LIST, ¶m_types_list + ); + let body_with_params_list = Self::substitute_param_in_string( + &body_with_param_types_list, INPUT_PARAMS_LIST, ¶ms_list + ); + if verbose { + eprintln!("Body of 'main.c' after input substitution:\n{}", body_with_params_list); + } + body_with_params_list + } + + fn get_usage_args_string(params: &[LLVMTypeRef]) -> String { + let mut args_string = String::new(); + for i in 0..params.len() { + let sep = if i == 0 { "" } else { ", " }; + args_string = args_string + sep + format!("", i).as_str(); + } + args_string + } + + fn collect_callee_param_decls_string(params: &[LLVMTypeRef], indent_depth: usize) -> String { + let mut decls: Vec = Vec::new(); + for (i, t) in params.iter().enumerate() { + decls.push(Self::get_callee_param_decl_string(i, *t)); + } + let mut join_str = String::from("\n"); + for _ in 0..indent_depth { + join_str += " "; + } + decls.join(join_str.as_str()) + } + + /// Generates an assignment to a constant integer from a call to atoll + fn get_callee_param_decl_string(idx: usize, t: LLVMTypeRef) -> String { + let t_str = format!("t_{}", ModuleBundle::type_name_from(t)); + format!("const {} p{} = ({})atoll(argv[BASE + {}]);", t_str, idx, t_str, idx) + } + + fn get_callee_param_types_list_string(params: &[LLVMTypeRef]) -> String { + let mut params_string = String::new(); + for (i, t) in params.iter().enumerate() { + let sep = if i == 0 { "" } else { ", " }; + let t_name = format!("t_{}", ModuleBundle::type_name_from(*t)); + params_string = params_string + sep + &t_name; + } + params_string + } + + fn get_callee_params_list_string(params: &[LLVMTypeRef]) -> String { + let n = params.len(); + let mut params_string = String::new(); + for i in 0..n { + let sep = if i == 0 { "" } else { ", " }; + params_string = params_string + sep + format!("p{}", i).as_str(); + } + params_string + } + + fn substitute_param_in_string(body: &str, old: &str, new: &str) -> String { + str::replace(body, old, new).to_string() + } +} diff --git a/src/module.rs b/src/module.rs index 7bac11c..6e0a2d7 100644 --- a/src/module.rs +++ b/src/module.rs @@ -3,10 +3,28 @@ extern crate llvm_sys as llvm; -use llvm::core::*; use llvm::analysis::LLVMVerifierFailureAction; use llvm::analysis::LLVMVerifyModule; use llvm::bit_writer::LLVMWriteBitcodeToFile; +use llvm::core::LLVMAddFunction; +use llvm::core::LLVMBuildAlloca; +use llvm::core::LLVMBuildGlobalString; +use llvm::core::LLVMConstInt; +use llvm::core::LLVMContextCreate; +use llvm::core::LLVMContextDispose; +use llvm::core::LLVMCreateBuilderInContext; +use llvm::core::LLVMDisposeBuilder; +use llvm::core::LLVMDisposeMessage; +use llvm::core::LLVMDisposeModule; +use llvm::core::LLVMFunctionType; +use llvm::core::LLVMGetIntTypeWidth; +use llvm::core::LLVMGetTypeKind; +use llvm::core::LLVMInt32TypeInContext; +use llvm::core::LLVMInt64TypeInContext; +use llvm::core::LLVMModuleCreateWithNameInContext; +use llvm::core::LLVMPointerTypeInContext; +use llvm::core::LLVMPrintModuleToString; +use llvm::core::LLVMSetSourceFileName; use llvm::linker::LLVMLinkModules2; use llvm::prelude::LLVMBool; use llvm::prelude::LLVMBuilderRef; @@ -14,8 +32,10 @@ use llvm::prelude::LLVMContextRef; use llvm::prelude::LLVMModuleRef; use llvm::prelude::LLVMTypeRef; use llvm::prelude::LLVMValueRef; +use llvm::LLVMTypeKind; use std::collections::HashMap; +use std::env; use std::ffi::c_char; use std::ffi::c_int; use std::ffi::c_uint; @@ -23,12 +43,21 @@ use std::ffi::c_ulonglong; use std::ffi::CStr; use std::fmt; use std::fmt::Display; +use std::fs::File; +use std::io::Write; +use std::path::Path; use std::ptr; +use crate::command; use crate::exit_code; +use crate::options; +use command::Command; use exit_code::exit; use exit_code::ExitCode; +use options::CodeGenType; +use options::OutputType; +use options::RunOptions; #[derive(Clone)] pub struct FunctionSignature { @@ -45,35 +74,37 @@ impl FunctionSignature { pub struct ModuleBundle<'a> { pub builder: LLVMBuilderRef, pub context: LLVMContextRef, + pub f: Option, + pub f_sig: Option, pub module: LLVMModuleRef, pub name: &'a String, + pub objects: Vec, pub scope: Scope, pub t_i32: LLVMTypeRef, pub t_i64: LLVMTypeRef, pub t_opaque: LLVMTypeRef, - pub f: Option, - pub f_sig: Option, pub verbose: bool, } impl <'a> ModuleBundle<'a> { pub fn new(name: &'a String, verbose: bool) -> Self { let bundle = unsafe { - let c = LLVMContextCreate(); + let context = LLVMContextCreate(); let n = Self::value_name(name.as_str()); - let m = LLVMModuleCreateWithNameInContext(n.as_ptr() as *const c_char, c); - let b = LLVMCreateBuilderInContext(c); + let module = LLVMModuleCreateWithNameInContext(n.as_ptr() as *const c_char, context); + let builder = LLVMCreateBuilderInContext(context); ModuleBundle{ - builder: b, - context: c, - module: m, - name, - scope: Scope::new(), - t_i32: LLVMInt32TypeInContext(c), - t_i64: LLVMInt64TypeInContext(c), - t_opaque: LLVMPointerTypeInContext(c, 0 as c_uint), + builder, + context, f: None, f_sig: None, + module, + name, + objects: Vec::new(), + scope: Scope::new(), + t_i32: LLVMInt32TypeInContext(context), + t_i64: LLVMInt64TypeInContext(context), + t_opaque: LLVMPointerTypeInContext(context, 0 as c_uint), verbose, } }; @@ -167,13 +198,153 @@ impl <'a> ModuleBundle<'a> { } pub fn write_bitcode_to_file(&mut self, f: &str) -> () { - let name = String::from(f) + "\0"; + let name = format!("{}\0", f); let result: c_int = unsafe { LLVMWriteBitcodeToFile(self.module, name.as_ptr() as *const c_char) }; if result != 0 as c_int { eprintln!("Failed to write module to file '{}'", name); exit(ExitCode::WriteError); } } + + pub fn set_sourcefile_name(&mut self, f: &str) -> () { + unsafe { + LLVMSetSourceFileName(self.module, f.as_ptr() as *const c_char, f.len()); + } + } + + pub fn get_int_width(t: LLVMTypeRef) -> usize { + let kind: LLVMTypeKind = unsafe { LLVMGetTypeKind(t) }; + if kind != LLVMTypeKind::LLVMIntegerTypeKind { + eprintln!("Unexpected non-integer type"); + exit(ExitCode::ModuleError); + }; + (unsafe { LLVMGetIntTypeWidth(t) }) as usize + } + + pub fn type_name_from(t: LLVMTypeRef) -> String { + let kind: LLVMTypeKind = unsafe { LLVMGetTypeKind(t) }; + match kind { + LLVMTypeKind::LLVMPointerTypeKind => "ptr", + LLVMTypeKind::LLVMIntegerTypeKind => { + match Self::get_int_width(t) { + 32 => "i32", + 64 => "i64", + _ => { + eprintln!("Unsupported integer type width"); + exit(ExitCode::ModuleError); + }, + } + } + _ => { + eprintln!("Unsupported type kind"); + exit(ExitCode::ModuleError); + } + }.to_string() + } + + /// Objects required for linking the final object file/executable should be pushed before + /// output + pub fn push_object(&mut self, obj_path: String) -> () { + let path = Path::new(&obj_path); + if !path.is_file() || ".o" == path.extension().unwrap() { + eprintln!("Expected object file '{}'", obj_path); + exit(ExitCode::ModuleError); + } + self.objects.push(obj_path); + } + + pub fn write_module(&mut self, options: &RunOptions, output: &OutputType) -> bool { + match *output { + OutputType::Stdout => { + match options.codegen_type { + CodeGenType::Llvmir => println!("{}", self), + _ => { + eprintln!("Unimplemented: writing {} to Stdout", options.codegen_type); + return false; + }, + } + }, + OutputType::File(f) => { + match options.codegen_type { + CodeGenType::Llvmir => { + let string: String = self.to_string(); + let mut file = match File::create(f) { + Ok(file) => file, + Err(msg) => { + eprintln!("Failed to open output file '{}': {}", f, msg); + return false; + } + }; + match file.write_all(string.as_bytes()) { + Ok(()) => (), + Err(msg) => { + eprintln!("Failed to write to output file '{}': {}", f, msg); + return false; + } + } + }, + CodeGenType::Bitcode => self.write_bitcode_to_file(f), + CodeGenType::Object => { + let f_path = Path::new(f); + let temp_dir = env::temp_dir(); + let f_stem = f_path.file_stem().unwrap().to_str().unwrap(); + let f_bc = temp_dir.join(format!("{}.bc", f_stem)); + let f_bc_str = f_bc.to_str().unwrap(); + self.write_bitcode_to_file(f_bc_str); + self.object_file_from_bitcode(f, f_bc_str); + }, + CodeGenType::Executable => { + let f_path = Path::new(f); + let temp_dir = env::temp_dir(); + let f_stem = f_path.file_stem().unwrap().to_str().unwrap(); + let f_bc = temp_dir.join(format!("{}.bc", f_stem)); + let f_bc_str = f_bc.to_str().unwrap(); + self.write_bitcode_to_file(f_bc_str); + let f_obj = temp_dir.join(format!("{}.obj", f_stem)); + let f_obj_str = f_obj.to_str().unwrap(); + self.object_file_from_bitcode(f_obj_str, f_bc_str); + self.executable_file_from_object(f, f_obj_str); + }, + CodeGenType::Unset => { + eprintln!("Cannot write module with unset codegen type"); + exit(ExitCode::WriteError); + }, + } + }, + } + true + } + + fn object_file_from_bitcode(&self, f_obj: &str, f_bc: &str) -> () { + let arg_output = format!("-o={}", f_obj); + let args: Vec<&str> = vec![ + "-relocation-model=pic", + "-filetype=obj", + arg_output.as_str(), + f_bc, + ]; + let result_llc = Command::run("llc", &args); + if !result_llc.success { + eprintln!("Failed to write object file '{}' from bitcode file '{}'", f_obj, f_bc); + exit(ExitCode::WriteError); + } else if result_llc.stdout.is_some() { + println!("{}", result_llc.stdout.unwrap()); + } + } + + fn executable_file_from_object(&self, f_bin: &str, f_obj: &str) -> () { + let mut args: Vec<&str> = vec!["-o", f_bin, f_obj]; + for object in self.objects.iter() { + args.push(object.as_str()); + } + let result_clang = Command::run("clang", &args); + if !result_clang.success { + eprintln!("Failed to write executable file '{}' from object file '{}'", f_bin, f_obj); + exit(ExitCode::WriteError); + } else if result_clang.stdout.is_some() { + println!("{}", result_clang.stdout.unwrap()); + } + } } impl <'a> Display for ModuleBundle<'a> { @@ -192,7 +363,6 @@ impl <'a> Display for ModuleBundle<'a> { } } - impl <'a> Drop for ModuleBundle<'a> { fn drop(&mut self) -> () { self.scope.clear(); diff --git a/src/options.rs b/src/options.rs index 514ae58..999c236 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,11 +1,16 @@ // Copyright 2024, Giordano Salvador // SPDX-License-Identifier: BSD-3-Clause +use std::fmt; + pub struct RunOptions { + pub body_type: BodyType, pub codegen_type: CodeGenType, pub drop_token: bool, + pub host_arch: HostArch, + pub host_os: HostOS, + pub ir_exit: bool, pub lex_exit: bool, - pub no_main: bool, pub no_target: bool, pub opt_level: OptLevel, pub parse_exit: bool, @@ -17,10 +22,13 @@ pub struct RunOptions { impl RunOptions { pub fn new() -> Self { RunOptions{ - codegen_type: CodeGenType::Bytecode, + body_type: BodyType::Unset, + codegen_type: CodeGenType::Unset, drop_token: false, + host_arch: get_host_arch(), + host_os: get_host_os(), + ir_exit: false, lex_exit: false, - no_main: false, no_target: false, opt_level: OptLevel::O2, parse_exit: false, @@ -31,27 +39,176 @@ impl RunOptions { } } -#[repr(u8)] +impl fmt::Display for RunOptions { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s_vec = vec![ + "RunOptions:".to_string(), + format!("body_type: {}", self.body_type), + format!("codegen_type: {}", self.codegen_type), + format!("drop_token: {}", self.drop_token), + format!("host_arch: {}", self.host_arch), + format!("host_os: {}", self.host_os), + format!("ir_exit: {}", self.ir_exit), + format!("lex_exit: {}", self.lex_exit), + format!("no_target: {}", self.no_target), + format!("opt_level: {}", self.opt_level), + format!("parse_exit: {}", self.parse_exit), + format!("print_ast: {}", self.print_ast), + format!("sem_exit: {}", self.sem_exit), + format!("verbose: {}", self.verbose), + ]; + write!(f, "{}", s_vec.join("\n ")) + } +} + #[derive(Clone,Copy)] +pub enum OutputType<'a> { + Stdout, + File(&'a str), +} + +impl <'a> fmt::Display for OutputType<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + OutputType::Stdout => "Stdout".to_string(), + OutputType::File(f) => format!("File:{}", f), + }; + write!(f, "{}", s) + } +} + +#[repr(u8)] +#[derive(Clone,Copy,Default)] pub enum OptLevel { O0 = 0, O1 = 1, - O2 = 2, /// LLVM default opt level + #[default] + O2 = 2, /// LLVM default opt level O3 = 3, } -pub fn opt_level_to_str(opt_level: OptLevel) -> String { - String::from(match opt_level { - OptLevel::O0 => "O0", - OptLevel::O1 => "O1", - OptLevel::O2 => "O2", - OptLevel::O3 => "O3", - }) +impl fmt::Display for OptLevel { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + OptLevel::O0 => "OptLevel_O0", + OptLevel::O1 => "OptLevel_O1", + OptLevel::O2 => "OptLevel_O2", + OptLevel::O3 => "OptLevel_O3", + }; + write!(f, "{}", s) + } +} + +#[repr(u8)] +#[derive(Clone,Copy,Default,PartialEq)] +pub enum BodyType { + #[default] + Unset = 0, + MainGen = 1, + MainGenC = 2, + NoMain = 3, +} + +impl fmt::Display for BodyType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + BodyType::Unset => "BodyType_Unset", + BodyType::MainGen => "BodyType_MainGen", + BodyType::MainGenC => "BodyType_MainGenC", + BodyType::NoMain => "BodyType_NoMain", + }; + write!(f, "{}", s) + } } #[repr(u8)] -#[derive(Clone,Copy,PartialEq)] +#[derive(Clone,Copy,Default,PartialEq)] pub enum CodeGenType { - Llvmir = 0, - Bytecode = 1, + #[default] + Unset = 0, + Llvmir = 1, + Bitcode = 2, + Object = 3, + Executable = 4, +} + +impl fmt::Display for CodeGenType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + CodeGenType::Unset => "CodeGen_Unset", + CodeGenType::Llvmir => "CodeGen_Llvmir", + CodeGenType::Bitcode => "CodeGen_Bitcode", + CodeGenType::Object => "CodeGen_Object", + CodeGenType::Executable => "CodeGen_Executable", + }; + write!(f, "{}", s) + } +} + +#[repr(u8)] +#[derive(Clone,Copy,Default,PartialEq)] +pub enum HostArch { + #[default] + Unknown = 0, + Aarch64 = 1, + X86 = 2, + X86_64 = 3, +} + +impl fmt::Display for HostArch { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + HostArch::Unknown => "HostArch_Unknown", + HostArch::Aarch64 => "HostArch_Aarch64", + HostArch::X86 => "HostArch_X86", + HostArch::X86_64 => "HostArch_X86_64", + }; + write!(f, "{}", s) + } +} + +pub fn get_host_arch() -> HostArch { + if cfg!(target_arch = "aarch64") { + HostArch::Aarch64 + } else if cfg!(target_arch = "x86") { + HostArch::X86 + } else if cfg!(target_arch = "x86_64") { + HostArch::X86_64 + } else { + HostArch::Unknown + } +} + +#[repr(u8)] +#[derive(Clone,Copy,Default,PartialEq)] +pub enum HostOS { + #[default] + Unknown = 0, + Linux = 1, + MacOS = 2, + Windows = 3, +} + +impl fmt::Display for HostOS { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + HostOS::Unknown => "HostOS_Unknown", + HostOS::Linux => "HostOS_Linux", + HostOS::MacOS => "HostOS_MacOS", + HostOS::Windows => "HostOS_Windows", + }; + write!(f, "{}", s) + } +} + +pub fn get_host_os() -> HostOS { + if cfg!(target_os = "linux") { + HostOS::Linux + } else if cfg!(target_os = "macos") { + HostOS::MacOS + } else if cfg!(target_os = "windows") { + HostOS::Windows + } else { + HostOS::Unknown + } } diff --git a/src/target.rs b/src/target.rs index fc7af4d..bfd5de0 100644 --- a/src/target.rs +++ b/src/target.rs @@ -8,8 +8,31 @@ use llvm::error::LLVMDisposeErrorMessage; use llvm::error::LLVMGetErrorMessage; use llvm::core::LLVMDisposeMessage; use llvm::core::LLVMSetTarget; -use llvm::target::*; -use llvm::target_machine::*; +use llvm::target::LLVM_InitializeAllAsmParsers; +use llvm::target::LLVM_InitializeAllAsmPrinters; +use llvm::target::LLVM_InitializeAllTargetInfos; +use llvm::target::LLVM_InitializeAllTargetMCs; +use llvm::target::LLVM_InitializeAllTargets; +use llvm::target::LLVMDisposeTargetData; +use llvm::target::LLVMSetModuleDataLayout; +use llvm::target::LLVMTargetDataRef; +use llvm::target_machine::LLVMCreateTargetDataLayout; +use llvm::target_machine::LLVMCreateTargetMachineOptions; +use llvm::target_machine::LLVMCreateTargetMachineWithOptions; +use llvm::target_machine::LLVMCodeGenOptLevel; +use llvm::target_machine::LLVMCodeModel; +use llvm::target_machine::LLVMDisposeTargetMachine; +use llvm::target_machine::LLVMDisposeTargetMachineOptions; +use llvm::target_machine::LLVMGetDefaultTargetTriple; +use llvm::target_machine::LLVMGetFirstTarget; +use llvm::target_machine::LLVMGetTargetFromTriple; +use llvm::target_machine::LLVMRelocMode; +use llvm::target_machine::LLVMTargetMachineOptionsRef; +use llvm::target_machine::LLVMTargetMachineOptionsSetCodeGenOptLevel; +use llvm::target_machine::LLVMTargetMachineOptionsSetCodeModel; +use llvm::target_machine::LLVMTargetMachineOptionsSetRelocMode; +use llvm::target_machine::LLVMTargetMachineRef; +use llvm::target_machine::LLVMTargetRef; use llvm::transforms::pass_builder::*; @@ -24,7 +47,6 @@ use crate::options; use exit_code::exit; use exit_code::ExitCode; use module::ModuleBundle; -use options::opt_level_to_str; use options::OptLevel; static mut INITIALIZED: bool = false; @@ -147,6 +169,13 @@ impl <'a> Drop for TargetMachine<'a> { } } +#[allow(dead_code)] +#[derive(Clone)] +pub enum Passes { + Default(OptLevel), + Pipeline(String), +} + pub struct PassBuilder { builder: LLVMPassBuilderOptionsRef, } @@ -161,10 +190,18 @@ impl PassBuilder { &mut self, bundle: &mut ModuleBundle, machine: &mut TargetMachine, - opt_level: OptLevel, + passes: Passes, no_target: bool, ) -> bool { - let passes: String = format!("default<{}>\0", opt_level_to_str(opt_level)); + let passes_str = match passes { + Passes::Default(opt_level) => format!("default<{}>\0", match opt_level { + OptLevel::O0 => "O0", + OptLevel::O1 => "O1", + OptLevel::O2 => "O2", + OptLevel::O3 => "O3", + }), + Passes::Pipeline(string) => format!("{}\0", string), + }; unsafe { if !no_target { LLVMSetModuleDataLayout(bundle.module, machine.data_layout); @@ -172,7 +209,7 @@ impl PassBuilder { }; let error: LLVMErrorRef = LLVMRunPasses( bundle.module, - passes.as_ptr() as *const c_char, + passes_str.as_ptr() as *const c_char, machine.machine, self.builder ); diff --git a/tests/lit-llvm/codegen_type.calc b/tests/lit-llvm/codegen_type.calc deleted file mode 100644 index 7b93b35..0000000 --- a/tests/lit-llvm/codegen_type.calc +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: not @calcc --nomain --notarget -e "10" -S -o %t.bc 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: not @calcc --nomain --notarget -e "10" -o %t.ll 2>&1 | @filecheck %s --check-prefix=CHECK_B - -// RUN: @calcc --nomain --notarget -e "10" -S -o %t0.ll && @llvm-as -o %t0.bc %t0.ll -// RUN: @calcc --nomain --notarget -e "10" -o %t1.bc && @llvm-dis -o %t1.ll %t1.bc -// RUN: @count -l %t0.ll | @filecheck %s --check-prefix=CHECK_C -// RUN: @count -l %t1.ll | @filecheck %s --check-prefix=CHECK_C -// RUN: @count -l %t0.bc | @filecheck %s --check-prefix=CHECK_C -// RUN: @count -l %t1.bc | @filecheck %s --check-prefix=CHECK_C - -// CHECK_A: LLVM IR file type (with '-S' flag) should match output name ('.ll' extension) -// CHECK_B: Bytecode file type (missing '-S' flag) should match output name ('.bc' extension) - -// CHECK_C-NOT: ^0 diff --git a/tests/lit-llvm/end_to_end1.calc b/tests/lit-llvm/end_to_end1.calc deleted file mode 100644 index 2c898e9..0000000 --- a/tests/lit-llvm/end_to_end1.calc +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: @calcc -e "4096" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out | @filecheck %s -// RUN: @calcc -e "1024*32/8" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out | @filecheck %s -// RUN: @calcc -e "with: a: a*32/8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 1024 | @filecheck %s -// RUN: @calcc -e "with: a: 1024*a/8" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 32 | @filecheck %s -// RUN: @calcc -e "with: a: 1024*32/a" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 8 | @filecheck %s - -// CHECK: calcc_main result: 4096 - -// XFAIL: OS_MACOS diff --git a/tests/lit-llvm/end_to_end2.calc b/tests/lit-llvm/end_to_end2.calc deleted file mode 100644 index 26c8ee0..0000000 --- a/tests/lit-llvm/end_to_end2.calc +++ /dev/null @@ -1,49 +0,0 @@ -// RUN: @calcc -e "with: N1: N1 + 8" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0 -// RUN: @calcc -e "with: N1: 8 + N1" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1 - -// RUN: @calcc -e "with: N1: N1 - 8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2 -// RUN: @calcc -e "with: N1: 8 - N1" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3 -// RUN: @calcc -e "with: N1: -N1 + 8" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4 -// RUN: @calcc -e "with: N1: 8 + -N1" -o %t5.bc && @clang -O0 -o %t5.out %t5.bc && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5 - -// RUN: @calcc -e "with: N1: N1 * 8" -o %t6.bc && @clang -O0 -o %t6.out %t6.bc && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6 -// RUN: @calcc -e "with: N1: 8 * N1" -o %t7.bc && @clang -O0 -o %t7.out %t7.bc && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7 - -// RUN: @calcc -e "with: N1: -N1 * 8" -o %t8.bc && @clang -O0 -o %t8.out %t8.bc && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8 -// RUN: @calcc -e "with: N1: 8 * -N1" -o %t9.bc && @clang -O0 -o %t9.out %t9.bc && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9 - -// RUN: @calcc -e "with: N1: N1 / 8" -o %tA.bc && @clang -O0 -o %tA.out %tA.bc && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc -e "with: N1: 8 / N1" -o %tB.bc && @clang -O0 -o %tB.out %tB.bc && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B - -// RUN: @calcc -e "with: N1: -N1 / 8" -o %tC.bc && @clang -O0 -o %tC.out %tC.bc && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc -e "with: N1: 8 / -N1" -o %tD.bc && @clang -O0 -o %tD.out %tD.bc && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D - -// RUN: @calcc -O0 -e "with: N1: -(N1*2) / 8" -o %tE0.bc && @clang -O0 -o %tE0.out %tE0.bc && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E -// RUN: @calcc -e "with: N1: -(N1*2) / 8" -o %tE.bc && @clang -O0 -o %tE.out %tE.bc && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E -// RUN: @calcc -O0 -e "with: N1: 8 / -(N1*2)" -o %tF0.bc && @clang -O0 -o %tF0.out %tF0.bc && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F -// RUN: @calcc -e "with: N1: 8 / -(N1*2)" -o %tF.bc && @clang -O0 -o %tF.out %tF.bc && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F - -// CHECK_0: calcc_main result: 23 -// CHECK_1: calcc_main result: 23 - -// CHECK_2: calcc_main result: 7 -// CHECK_3: calcc_main result: -7 -// CHECK_4: calcc_main result: -7 -// CHECK_5: calcc_main result: -7 - -// CHECK_6: calcc_main result: 120 -// CHECK_7: calcc_main result: 120 - -// CHECK_8: calcc_main result: -120 -// CHECK_9: calcc_main result: -120 - -// CHECK_A: calcc_main result: 1 -// CHECK_B: calcc_main result: 0 - -// CHECK_C: calcc_main result: -1 -// CHECK_D: calcc_main result: 0 - -// CHECK_E: calcc_main result: -3 -// CHECK_F: calcc_main result: 0 - -// XFAIL: OS_MACOS diff --git a/tests/lit-llvm/lex_other.calc b/tests/lit-llvm/lex_other.calc deleted file mode 100644 index 78bfb0c..0000000 --- a/tests/lit-llvm/lex_other.calc +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: @calcc --verbose --lex -e ',:-()/*' 2>&1 | @filecheck %s - -// CHECK: Processing input 'Expression:,:-()/*' -// CHECK: Read 7 bytes from buffer at line 0 -// CHECK: Found char ',' in line 0 at pos 0 -// CHECK: Lexed token 'Comma:,' -// CHECK: Found char ':' in line 0 at pos 1 -// CHECK: Lexed token 'Colon::' -// CHECK: Found char '-' in line 0 at pos 2 -// CHECK: Lexed token 'Minus:-' -// CHECK: Found char '(' in line 0 at pos 3 -// CHECK: Lexed token 'ParenL:(' -// CHECK: Found char ')' in line 0 at pos 4 -// CHECK: Lexed token 'ParenR:)' -// CHECK: Found char '/' in line 0 at pos 5 -// CHECK: Lexed token 'Slash:/' -// CHECK: Found char '*' in line 0 at pos 6 -// CHECK: Lexed token 'Star:*' -// CHECK: Lexed token 'Eoi:' diff --git a/tests/lit-llvm/parse_eoi.calc b/tests/lit-llvm/parse_eoi.calc deleted file mode 100644 index 9194ced..0000000 --- a/tests/lit-llvm/parse_eoi.calc +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: not @calcc -v --parse -e "" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: not @calcc -v --parse -e "25 + " 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: not @calcc -v --parse -e "with: a" 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: not @calcc -v --parse -e "with: a: a +" 2>&1 | @filecheck %s --check-prefix=CHECK_D -// RUN: not @calcc -v --parse -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_E - -// CHECK_A: Lexed token 'Eoi:' -// CHECK_A: Unexpected token - -// CHECK_B: Lexed token 'Eoi:' -// CHECK_B: Unexpected token - -// CHECK_C: Lexed token 'Eoi:' -// CHECK_C: Expected 'Colon' token at position 3 - -// CHECK_D: Lexed token 'Eoi:' -// CHECK_D: Unexpected token - -// CHECK_E: Lexed token 'Comment:// comment' -// CHECK_E: Lexed token 'Eoi:' -// CHECK_E: Unexpected token diff --git a/tests/lit-llvm/parse_idents.calc b/tests/lit-llvm/parse_idents.calc deleted file mode 100644 index a76e093..0000000 --- a/tests/lit-llvm/parse_idents.calc +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: @calcc --verbose --ast --parse -e a 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --ast --parse -e point 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc --verbose --ast --parse -e Zpos 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc --verbose --ast --parse -e __file__ 2>&1 | @filecheck %s --check-prefix=CHECK_D -// RUN: @calcc --verbose --ast --parse -e tmp002 2>&1 | @filecheck %s --check-prefix=CHECK_E - -// CHECK_A: Consumed expected token 'Ident' at position '0' -// CHECK_A: AST: Ident(a) - -// CHECK_B: Consumed expected token 'Ident' at position '0' -// CHECK_B: AST: Ident(point) - -// CHECK_C: Consumed expected token 'Ident' at position '0' -// CHECK_C: AST: Ident(Zpos) - -// CHECK_D: Consumed expected token 'Ident' at position '0' -// CHECK_D: AST: Ident(__file__) - -// CHECK_E: Consumed expected token 'Ident' at position '0' -// CHECK_E: AST: Ident(tmp002) diff --git a/tests/lit-llvm/parse_numbers.calc b/tests/lit-llvm/parse_numbers.calc deleted file mode 100644 index 6b6c0a9..0000000 --- a/tests/lit-llvm/parse_numbers.calc +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: @calcc --verbose --ast --parse -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --ast --parse -e 9 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc --verbose --ast --parse -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc --verbose --ast --parse -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc --verbose --ast --parse -e "0x7FFFFFFFFFFFFFFF" 2>&1 | @filecheck %s --check-prefix=CHECK_D - -// RUN: not @calcc --verbose --parse -e "0x8000000000000000" 2>&1 | @filecheck %s --check-prefix=CHECK_E -// RUN: not @calcc --verbose --parse -e "9223372036854775808" 2>&1 | @filecheck %s --check-prefix=CHECK_F -// RUN: not @calcc --verbose --parse -e "-9223372036854775809" 2>&1 | @filecheck %s --check-prefix=CHECK_G - -// CHECK_A: Consumed expected token 'Number' at position '0' -// CHECK_A: AST: 256 - -// CHECK_B: Consumed expected token 'Number' at position '0' -// CHECK_B: AST: 9 - -// CHECK_C: Consumed expected token 'Minus' at position '0' -// CHECK_C: Consumed expected token 'Number' at position '1' -// CHECK_C: AST: -9 - -// CHECK_D: Consumed expected token 'Number' at position '0' -// CHECK_D: AST: 9223372036854775807 - -// CHECK_E: Number '0x8000000000000000' failed parse: number too large to fit in target type -// CHECK_E: Failed to convert hexadecimal string - -// CHECK_F: Number '9223372036854775808' failed parse: number too large to fit in target type -// CHECK_F: Failed to convert decimal string - -// CHECK_G: Number '-9223372036854775809' failed parse: number too small to fit in target type -// CHECK_G: Failed to convert decimal string diff --git a/tests/lit-rust/lex_hello.calc b/tests/lit-rust/lex_hello.calc deleted file mode 100644 index e18fc8c..0000000 --- a/tests/lit-rust/lex_hello.calc +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: @calcc --verbose --lex -e hello -// RUN: @calcc --verbose --lex -e=hello -// RUN: @calcc --verbose --lex --expr hello -// RUN: @calcc --verbose --lex --expr=hello - -// CHECK: Processing input 'Expression:hello' -// CHECK: Read 5 bytes from buffer at line 0 -// CHECK: Found char 'h' in line 0 at pos 0 -// CHECK: Found char 'e' in line 0 at pos 1 -// CHECK: Found char 'l' in line 0 at pos 2 -// CHECK: Found char 'l' in line 0 at pos 3 -// CHECK: Found char 'o' in line 0 at pos 4 -// CHECK: Lexed token 'Ident:hello' -// CHECK: Lexed token 'Eoi:' diff --git a/tests/lit-rust/lex_number.calc b/tests/lit-rust/lex_number.calc deleted file mode 100644 index fa18b3f..0000000 --- a/tests/lit-rust/lex_number.calc +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: @calcc --verbose --lex -e 256 - -// CHECK: Processing input 'Expression:256' -// CHECK: Read 3 bytes from buffer at line 0 -// CHECK: Found char '2' in line 0 at pos 0 -// CHECK: Found char '5' in line 0 at pos 1 -// CHECK: Found char '6' in line 0 at pos 2 -// CHECK: Lexed token 'Number:256' -// CHECK: Lexed token 'Eoi:' diff --git a/tests/lit-rust/usage.calc b/tests/lit-rust/usage.calc deleted file mode 100644 index 81d7af4..0000000 --- a/tests/lit-rust/usage.calc +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: @calcc -h -// RUN: @calcc --help - -// CHECK: usage: calcc [OPTIONS] diff --git a/tests/lit-rust/version.calc b/tests/lit-rust/version.calc deleted file mode 100644 index 4ee8574..0000000 --- a/tests/lit-rust/version.calc +++ /dev/null @@ -1,5 +0,0 @@ -// RUN: @calcc --version - -// CHECK: Welcome to calcc version v0.1.0-dev -// CHECK: // Copyright 2024, Giordano Salvador -// CHECK: // SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/lit-tests-rust.rs b/tests/lit-tests-rust.rs deleted file mode 100644 index 68eadef..0000000 --- a/tests/lit-tests-rust.rs +++ /dev/null @@ -1,39 +0,0 @@ -extern crate lit; - -#[allow(dead_code)] -#[cfg(test)] -mod tests{ - use std::env; - use std::path::Path; - use std::path::PathBuf; - - fn path_to_string(path: &PathBuf) -> String { - path.to_str().unwrap().to_string() - } - - fn get_bin_dir() -> PathBuf { - env::current_exe().ok().map(|mut path: PathBuf| { - path.pop(); - path.pop(); - path - }).unwrap() - } - - fn get_calcc() -> String { - path_to_string(&get_bin_dir().join( - format!("{}{}", "calcc", env::consts::EXE_SUFFIX) - )) - } - - // Disabled -- #[test] - fn lit() { - lit::run::tests( - lit::event_handler::Default::new(), - |config: &mut lit::config::Config| { - config.add_search_path(Path::new("tests").join("lit-rust").to_str().unwrap()); - config.add_extension("calc"); - config.constants.insert("calcc".to_owned(), get_calcc()); - } - ).expect("Lit tests failed") ; - } -} diff --git a/tests/lit-tests-llvm.rs b/tests/lit-tests.rs similarity index 98% rename from tests/lit-tests-llvm.rs rename to tests/lit-tests.rs index 350b5ba..ca57806 100644 --- a/tests/lit-tests-llvm.rs +++ b/tests/lit-tests.rs @@ -126,7 +126,7 @@ mod tests{ let lit_bin: &Path = Path::new(lit_bin_str.as_str()); let shell: String = get_shell(&os_name); let tests_dir: PathBuf = get_tests_dir(); - let lit_dir: PathBuf = tests_dir.join("lit-llvm"); + let lit_dir: PathBuf = tests_dir.join("lit-tests"); let cfg_path: PathBuf = lit_dir.join("lit.cfg"); println!("Lit binary path: {}", lit_bin_str); diff --git a/tests/lit-tests/codegen_type.calc b/tests/lit-tests/codegen_type.calc new file mode 100644 index 0000000..4097583 --- /dev/null +++ b/tests/lit-tests/codegen_type.calc @@ -0,0 +1,28 @@ +// RUN: not @calcc -k -e "10" -S -o %t.bc 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: not @calcc -k -e "10" -b -o %t.ll 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: not @calcc -k -e "10" -b -o %t.o 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: not @calcc -k -e "10" -b -o %t.exe 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: not @calcc -k -e "10" -b -o %t 2>&1 | @filecheck %s --check-prefix=CHECK_E + +// RUN: @calcc -k -e "10" -S -o %t0.ll && @llvm-as -o %t0.bc %t0.ll +// RUN: @calcc -k -e "10" -b -o %t1.bc && @llvm-dis -o %t1.ll %t1.bc +// RUN: @calcc -e "10" -c -o %t1.o +// RUN: @calcc -e "10" -o %t1.exe +// RUN: @calcc -e "10" -o %t1 +// RUN: @count -l %t0.ll | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.ll | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t0.bc | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.bc | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.o | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.exe | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1 | @filecheck %s --check-prefix=CHECK_F + +// UNSUPPORTED: OS_MACOS + +// CHECK_A: Output name ('.bc' extension) should match codegen type (-b|--bitcode) +// CHECK_B: Output name ('.ll' extension) should match codegen type (-S|--llvmir) +// CHECK_C: Output name ('.o' extension) should match codegen type (-c) +// CHECK_D: Output name ('.exe' extension) should match codegen type: CodeGen_Bitcode specified +// CHECK_E: Output name (no/unknown extension) should match codegen type: CodeGen_Bitcode specified + +// CHECK_F-NOT: ^0 diff --git a/tests/lit-tests/codegen_type_c.calc b/tests/lit-tests/codegen_type_c.calc new file mode 100644 index 0000000..88bec94 --- /dev/null +++ b/tests/lit-tests/codegen_type_c.calc @@ -0,0 +1,26 @@ +// RUN: not @calcc -C -e "10" -S -o %t.bc 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: not @calcc -C -e "10" -b -o %t.ll 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: not @calcc -C -e "10" -b -o %t.o 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: not @calcc -C -e "10" -b -o %t.exe 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: not @calcc -C -e "10" -b -o %t 2>&1 | @filecheck %s --check-prefix=CHECK_E + +// RUN: @calcc -C -e "10" -S -o %t0.ll && @llvm-as -o %t0.bc %t0.ll +// RUN: @calcc -C -e "10" -b -o %t1.bc && @llvm-dis -o %t1.ll %t1.bc +// RUN: @calcc -C -e "10" -c -o %t1.o +// RUN: @calcc -C -e "10" -o %t1.exe +// RUN: @calcc -C -e "10" -o %t1 +// RUN: @count -l %t0.ll | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.ll | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t0.bc | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.bc | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.o | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1.exe | @filecheck %s --check-prefix=CHECK_F +// RUN: @count -l %t1 | @filecheck %s --check-prefix=CHECK_F + +// CHECK_A: Output name ('.bc' extension) should match codegen type (-b|--bitcode) +// CHECK_B: Output name ('.ll' extension) should match codegen type (-S|--llvmir) +// CHECK_C: Output name ('.o' extension) should match codegen type (-c) +// CHECK_D: Output name ('.exe' extension) should match codegen type: CodeGen_Bitcode specified +// CHECK_E: Output name (no/unknown extension) should match codegen type: CodeGen_Bitcode specified + +// CHECK_F-NOT: ^0 diff --git a/tests/lit-tests/end_to_end1.calc b/tests/lit-tests/end_to_end1.calc new file mode 100644 index 0000000..55529e1 --- /dev/null +++ b/tests/lit-tests/end_to_end1.calc @@ -0,0 +1,8 @@ +// RUN: @calcc -b -e "4096" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out | @filecheck %s +// RUN: @calcc -b -e "1024*32/8" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out | @filecheck %s +// RUN: @calcc -b -e "with: a: a*32/8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 1024 | @filecheck %s +// RUN: @calcc -b -e "with: a: 1024*a/8" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 32 | @filecheck %s +// RUN: @calcc -b -e "with: a: 1024*32/a" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 8 | @filecheck %s + +// UNSUPPORTED: OS_MACOS +// CHECK: calcc_main result: 4096 diff --git a/tests/lit-tests/end_to_end2.calc b/tests/lit-tests/end_to_end2.calc new file mode 100644 index 0000000..5c22bd9 --- /dev/null +++ b/tests/lit-tests/end_to_end2.calc @@ -0,0 +1,49 @@ +// RUN: @calcc -b -e "with: N1: N1 + 8" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0 +// RUN: @calcc -b -e "with: N1: 8 + N1" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1 + +// RUN: @calcc -b -e "with: N1: N1 - 8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2 +// RUN: @calcc -b -e "with: N1: 8 - N1" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3 +// RUN: @calcc -b -e "with: N1: -N1 + 8" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4 +// RUN: @calcc -b -e "with: N1: 8 + -N1" -o %t5.bc && @clang -O0 -o %t5.out %t5.bc && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5 + +// RUN: @calcc -b -e "with: N1: N1 * 8" -o %t6.bc && @clang -O0 -o %t6.out %t6.bc && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6 +// RUN: @calcc -b -e "with: N1: 8 * N1" -o %t7.bc && @clang -O0 -o %t7.out %t7.bc && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7 + +// RUN: @calcc -b -e "with: N1: -N1 * 8" -o %t8.bc && @clang -O0 -o %t8.out %t8.bc && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8 +// RUN: @calcc -b -e "with: N1: 8 * -N1" -o %t9.bc && @clang -O0 -o %t9.out %t9.bc && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9 + +// RUN: @calcc -b -e "with: N1: N1 / 8" -o %tA.bc && @clang -O0 -o %tA.out %tA.bc && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc -b -e "with: N1: 8 / N1" -o %tB.bc && @clang -O0 -o %tB.out %tB.bc && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B + +// RUN: @calcc -b -e "with: N1: -N1 / 8" -o %tC.bc && @clang -O0 -o %tC.out %tC.bc && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc -b -e "with: N1: 8 / -N1" -o %tD.bc && @clang -O0 -o %tD.out %tD.bc && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D + +// RUN: @calcc -O0 -b -e "with: N1: -(N1*2) / 8" -o %tE0.bc && @clang -O0 -o %tE0.out %tE0.bc && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc -b -e "with: N1: -(N1*2) / 8" -o %tE.bc && @clang -O0 -o %tE.out %tE.bc && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc -O0 -b -e "with: N1: 8 / -(N1*2)" -o %tF0.bc && @clang -O0 -o %tF0.out %tF0.bc && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F +// RUN: @calcc -b -e "with: N1: 8 / -(N1*2)" -o %tF.bc && @clang -O0 -o %tF.out %tF.bc && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F + +// UNSUPPORTED: OS_MACOS + +// CHECK_0: calcc_main result: 23 +// CHECK_1: calcc_main result: 23 + +// CHECK_2: calcc_main result: 7 +// CHECK_3: calcc_main result: -7 +// CHECK_4: calcc_main result: -7 +// CHECK_5: calcc_main result: -7 + +// CHECK_6: calcc_main result: 120 +// CHECK_7: calcc_main result: 120 + +// CHECK_8: calcc_main result: -120 +// CHECK_9: calcc_main result: -120 + +// CHECK_A: calcc_main result: 1 +// CHECK_B: calcc_main result: 0 + +// CHECK_C: calcc_main result: -1 +// CHECK_D: calcc_main result: 0 + +// CHECK_E: calcc_main result: -3 +// CHECK_F: calcc_main result: 0 diff --git a/tests/lit-tests/end_to_end3.calc b/tests/lit-tests/end_to_end3.calc new file mode 100644 index 0000000..59498b8 --- /dev/null +++ b/tests/lit-tests/end_to_end3.calc @@ -0,0 +1,8 @@ +// RUN: @calcc -e "4096" -o %t0.out && %t0.out | @filecheck %s +// RUN: @calcc -e "1024*32/8" -o %t1.out && %t1.out | @filecheck %s +// RUN: @calcc -e "with: a: a*32/8" -o %t2.out && %t2.out 1024 | @filecheck %s +// RUN: @calcc -e "with: a: 1024*a/8" -o %t3.out && %t3.out 32 | @filecheck %s +// RUN: @calcc -e "with: a: 1024*32/a" -o %t4.out && %t4.out 8 | @filecheck %s + +// UNSUPPORTED: OS_MACOS +// CHECK: calcc_main result: 4096 diff --git a/tests/lit-tests/end_to_end4.calc b/tests/lit-tests/end_to_end4.calc new file mode 100644 index 0000000..21b0b52 --- /dev/null +++ b/tests/lit-tests/end_to_end4.calc @@ -0,0 +1,49 @@ +// RUN: @calcc -e "with: N1: N1 + 8" -o %t0.out && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0 +// RUN: @calcc -e "with: N1: 8 + N1" -o %t1.out && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1 + +// RUN: @calcc -e "with: N1: N1 - 8" -o %t2.out && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2 +// RUN: @calcc -e "with: N1: 8 - N1" -o %t3.out && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3 +// RUN: @calcc -e "with: N1: -N1 + 8" -o %t4.out && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4 +// RUN: @calcc -e "with: N1: 8 + -N1" -o %t5.out && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5 + +// RUN: @calcc -e "with: N1: N1 * 8" -o %t6.out && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6 +// RUN: @calcc -e "with: N1: 8 * N1" -o %t7.out && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7 + +// RUN: @calcc -e "with: N1: -N1 * 8" -o %t8.out && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8 +// RUN: @calcc -e "with: N1: 8 * -N1" -o %t9.out && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9 + +// RUN: @calcc -e "with: N1: N1 / 8" -o %tA.out && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc -e "with: N1: 8 / N1" -o %tB.out && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B + +// RUN: @calcc -e "with: N1: -N1 / 8" -o %tC.out && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc -e "with: N1: 8 / -N1" -o %tD.out && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D + +// RUN: @calcc -O0 -e "with: N1: -(N1*2) / 8" -o %tE0.out && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc -e "with: N1: -(N1*2) / 8" -o %tE.out && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc -O0 -e "with: N1: 8 / -(N1*2)" -o %tF0.out && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F +// RUN: @calcc -e "with: N1: 8 / -(N1*2)" -o %tF.out && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F + +// UNSUPPORTED: OS_MACOS + +// CHECK_0: calcc_main result: 23 +// CHECK_1: calcc_main result: 23 + +// CHECK_2: calcc_main result: 7 +// CHECK_3: calcc_main result: -7 +// CHECK_4: calcc_main result: -7 +// CHECK_5: calcc_main result: -7 + +// CHECK_6: calcc_main result: 120 +// CHECK_7: calcc_main result: 120 + +// CHECK_8: calcc_main result: -120 +// CHECK_9: calcc_main result: -120 + +// CHECK_A: calcc_main result: 1 +// CHECK_B: calcc_main result: 0 + +// CHECK_C: calcc_main result: -1 +// CHECK_D: calcc_main result: 0 + +// CHECK_E: calcc_main result: -3 +// CHECK_F: calcc_main result: 0 diff --git a/tests/lit-tests/end_to_end_c1.calc b/tests/lit-tests/end_to_end_c1.calc new file mode 100644 index 0000000..4ad15f1 --- /dev/null +++ b/tests/lit-tests/end_to_end_c1.calc @@ -0,0 +1,7 @@ +// RUN: @calcc -C -e "4096" -o %t0.out && %t0.out | @filecheck %s +// RUN: @calcc -C -e "1024*32/8" -o %t1.out && %t1.out | @filecheck %s +// RUN: @calcc -C -e "with: a: a*32/8" -o %t2.out && %t2.out 1024 | @filecheck %s +// RUN: @calcc -C -e "with: a: 1024*a/8" -o %t3.out && %t3.out 32 | @filecheck %s +// RUN: @calcc -C -e "with: a: 1024*32/a" -o %t4.out && %t4.out 8 | @filecheck %s + +// CHECK: calcc_main result: 4096 diff --git a/tests/lit-tests/end_to_end_c2.calc b/tests/lit-tests/end_to_end_c2.calc new file mode 100644 index 0000000..80a87c6 --- /dev/null +++ b/tests/lit-tests/end_to_end_c2.calc @@ -0,0 +1,47 @@ +// RUN: @calcc -C -e "with: N1: N1 + 8" -o %t0.out && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0 +// RUN: @calcc -C -e "with: N1: 8 + N1" -o %t1.out && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1 + +// RUN: @calcc -C -e "with: N1: N1 - 8" -o %t2.out && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2 +// RUN: @calcc -C -e "with: N1: 8 - N1" -o %t3.out && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3 +// RUN: @calcc -C -e "with: N1: -N1 + 8" -o %t4.out && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4 +// RUN: @calcc -C -e "with: N1: 8 + -N1" -o %t5.out && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5 + +// RUN: @calcc -C -e "with: N1: N1 * 8" -o %t6.out && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6 +// RUN: @calcc -C -e "with: N1: 8 * N1" -o %t7.out && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7 + +// RUN: @calcc -C -e "with: N1: -N1 * 8" -o %t8.out && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8 +// RUN: @calcc -C -e "with: N1: 8 * -N1" -o %t9.out && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9 + +// RUN: @calcc -C -e "with: N1: N1 / 8" -o %tA.out && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc -C -e "with: N1: 8 / N1" -o %tB.out && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B + +// RUN: @calcc -C -e "with: N1: -N1 / 8" -o %tC.out && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc -C -e "with: N1: 8 / -N1" -o %tD.out && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D + +// RUN: @calcc -O0 -C -e "with: N1: -(N1*2) / 8" -o %tE0.out && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc -C -e "with: N1: -(N1*2) / 8" -o %tE.out && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc -O0 -C -e "with: N1: 8 / -(N1*2)" -o %tF0.out && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F +// RUN: @calcc -C -e "with: N1: 8 / -(N1*2)" -o %tF.out && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F + +// CHECK_0: calcc_main result: 23 +// CHECK_1: calcc_main result: 23 + +// CHECK_2: calcc_main result: 7 +// CHECK_3: calcc_main result: -7 +// CHECK_4: calcc_main result: -7 +// CHECK_5: calcc_main result: -7 + +// CHECK_6: calcc_main result: 120 +// CHECK_7: calcc_main result: 120 + +// CHECK_8: calcc_main result: -120 +// CHECK_9: calcc_main result: -120 + +// CHECK_A: calcc_main result: 1 +// CHECK_B: calcc_main result: 0 + +// CHECK_C: calcc_main result: -1 +// CHECK_D: calcc_main result: 0 + +// CHECK_E: calcc_main result: -3 +// CHECK_F: calcc_main result: 0 diff --git a/tests/lit-llvm/input_file1.calc b/tests/lit-tests/input_file1.calc similarity index 85% rename from tests/lit-llvm/input_file1.calc rename to tests/lit-tests/input_file1.calc index 614174e..896a1c5 100644 --- a/tests/lit-llvm/input_file1.calc +++ b/tests/lit-tests/input_file1.calc @@ -3,11 +3,8 @@ // RUN: @calcc -S -O0 %s -o %t2.ll && @clang -O0 -o %t2.out %t2.ll && %t2.out 6 8 10 | @filecheck %s // RUN: @calcc -S -O3 %s -o %t3.ll && @clang -O0 -o %t3.out %t3.ll && %t3.out 6 8 10 | @filecheck %s -// CHECK: calcc_main result: 0 - -// TODO: Comments are not implemented +// UNSUPPORTED: OS_MACOS +// CHECK: calcc_main result: 0 // Implement Pythagorean theorem check (for integer triangles): 0 result => passing check with: a,b,c: c*c - (a*a + b*b) - -// XFAIL: OS_MACOS diff --git a/tests/lit-tests/input_file_c1.calc b/tests/lit-tests/input_file_c1.calc new file mode 100644 index 0000000..aa25757 --- /dev/null +++ b/tests/lit-tests/input_file_c1.calc @@ -0,0 +1,9 @@ +// RUN: @calcc -C -O0 %s -o %t0.out && %t0.out 3 4 5 | @filecheck %s +// RUN: @calcc -C -O3 %s -o %t1.out && %t1.out 3 4 5 | @filecheck %s +// RUN: @calcc -C -O0 %s -o %t2.out && %t2.out 6 8 10 | @filecheck %s +// RUN: @calcc -C -O3 %s -o %t3.out && %t3.out 6 8 10 | @filecheck %s + +// CHECK: calcc_main result: 0 + +// Implement Pythagorean theorem check (for integer triangles): 0 result => passing check +with: a,b,c: c*c - (a*a + b*b) diff --git a/tests/lit-llvm/irgen1.calc b/tests/lit-tests/irgen1.calc similarity index 73% rename from tests/lit-llvm/irgen1.calc rename to tests/lit-tests/irgen1.calc index 5a54f37..73d4d33 100644 --- a/tests/lit-llvm/irgen1.calc +++ b/tests/lit-tests/irgen1.calc @@ -1,8 +1,8 @@ -// RUN: @calcc --nomain -O0 --llvmir -e "with: a,b: a+b + 10" | @filecheck %s -// RUN: @calcc --nomain -O0 -S -e "with: a,b: a+b + 10" | @filecheck %s +// RUN: @calcc --no-main -O0 --llvmir -e "with: a,b: a+b + 10" | @filecheck %s +// RUN: @calcc --no-main -O0 -S -e "with: a,b: a+b + 10" | @filecheck %s // CHECK-LABEL: ; ModuleID = 'calcc' -// CHECK: source_filename = "calcc" +// CHECK: source_filename = "-" // CHECK-LABEL: define i64 @calcc_main(i64 %0, i64 %1) { // CHECK-LABEL: entry: diff --git a/tests/lit-llvm/irgen2.calc b/tests/lit-tests/irgen2.calc similarity index 62% rename from tests/lit-llvm/irgen2.calc rename to tests/lit-tests/irgen2.calc index 79e7148..41a74f3 100644 --- a/tests/lit-llvm/irgen2.calc +++ b/tests/lit-tests/irgen2.calc @@ -1,11 +1,11 @@ -// RUN: @calcc --nomain -O0 --llvmir -e "1024*32/8" | @filecheck %s -// RUN: @calcc --nomain -O0 -S -e "1024*32/8" | @filecheck %s +// RUN: @calcc --no-main -O0 --llvmir -e "1024*32/8" | @filecheck %s +// RUN: @calcc --no-main -O0 -S -e "1024*32/8" | @filecheck %s /// The comprising arithmetic instructions are being optimized out /// (constant folding) between insertion in the module and the module dump call. // CHECK-LABEL: ; ModuleID = 'calcc' -// CHECK: source_filename = "calcc" +// CHECK: source_filename = "-" // CHECK-LABEL: define i64 @calcc_main() { // CHECK-LABEL: entry: diff --git a/tests/lit-llvm/lex_comment.calc b/tests/lit-tests/lex_comment.calc similarity index 88% rename from tests/lit-llvm/lex_comment.calc rename to tests/lit-tests/lex_comment.calc index 8124bfd..e1c7540 100644 --- a/tests/lit-llvm/lex_comment.calc +++ b/tests/lit-tests/lex_comment.calc @@ -1,6 +1,6 @@ -// RUN: @calcc -v --lex -e "//" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc -v --lex -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc -v --lex -e "with: a: 3/a // Divide by a" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc -v -k -S --lex -e "//" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc -v -k -S --lex -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc -v -k -S --lex -e "with: a: 3/a // Divide by a" 2>&1 | @filecheck %s --check-prefix=CHECK_C // CHECK_A: Processing input 'Expression://' // CHECK_A: Read 2 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_drop.calc b/tests/lit-tests/lex_drop.calc similarity index 81% rename from tests/lit-llvm/lex_drop.calc rename to tests/lit-tests/lex_drop.calc index 981d1e9..f66af49 100644 --- a/tests/lit-llvm/lex_drop.calc +++ b/tests/lit-tests/lex_drop.calc @@ -1,5 +1,5 @@ -// RUN: echo -n "hello $" | @calcc --verbose --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: echo -n "hello é" | not @calcc --verbose --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: echo -n "hello $" | @calcc --verbose -k -S --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: echo -n "hello é" | not @calcc --verbose -k -S --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_B // CHECK_A: Processing input 'Stdin' // CHECK_A: Read 7 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_hello.calc b/tests/lit-tests/lex_hello.calc similarity index 57% rename from tests/lit-llvm/lex_hello.calc rename to tests/lit-tests/lex_hello.calc index 0fff8c0..b868670 100644 --- a/tests/lit-llvm/lex_hello.calc +++ b/tests/lit-tests/lex_hello.calc @@ -1,7 +1,7 @@ -// RUN: @calcc --verbose --lex -e hello 2>&1 | @filecheck %s -// RUN: @calcc --verbose --lex -e=hello 2>&1 | @filecheck %s -// RUN: @calcc --verbose --lex --expr hello 2>&1 | @filecheck %s -// RUN: @calcc --verbose --lex --expr=hello 2>&1 | @filecheck %s +// RUN: @calcc --verbose -k -S --lex -e hello 2>&1 | @filecheck %s +// RUN: @calcc --verbose -k -S --lex -e=hello 2>&1 | @filecheck %s +// RUN: @calcc --verbose -k -S --lex --expr hello 2>&1 | @filecheck %s +// RUN: @calcc --verbose -k -S --lex --expr=hello 2>&1 | @filecheck %s // CHECK: Processing input 'Expression:hello' // CHECK: Read 5 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_idents.calc b/tests/lit-tests/lex_idents.calc similarity index 75% rename from tests/lit-llvm/lex_idents.calc rename to tests/lit-tests/lex_idents.calc index 1f6f484..d3ae159 100644 --- a/tests/lit-llvm/lex_idents.calc +++ b/tests/lit-tests/lex_idents.calc @@ -1,10 +1,10 @@ -// RUN: @calcc --verbose --lex -e "N" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --lex -e "_N" 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc --verbose --lex -e "N0" 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc --verbose --lex -e "N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_D -// RUN: @calcc --verbose --lex -e "_N0" 2>&1 | @filecheck %s --check-prefix=CHECK_E -// RUN: @calcc --verbose --lex -e "_N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_F -// RUN: @calcc --verbose --lex -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_G +// RUN: @calcc --verbose -k -S --lex -e "N" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --lex -e "_N" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose -k -S --lex -e "N0" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose -k -S --lex -e "N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: @calcc --verbose -k -S --lex -e "_N0" 2>&1 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc --verbose -k -S --lex -e "_N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_F +// RUN: @calcc --verbose -k -S --lex -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_G // CHECK_A: Processing input 'Expression:N' // CHECK_A: Read 1 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_nodrop.calc b/tests/lit-tests/lex_nodrop.calc similarity index 81% rename from tests/lit-llvm/lex_nodrop.calc rename to tests/lit-tests/lex_nodrop.calc index 0ab6ccd..6fcb3e0 100644 --- a/tests/lit-llvm/lex_nodrop.calc +++ b/tests/lit-tests/lex_nodrop.calc @@ -1,5 +1,5 @@ -// RUN: echo -n "hello $" | not @calcc --verbose --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: echo -n "hello é" | not @calcc --verbose --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: echo -n "hello $" | not @calcc --verbose -k -S --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: echo -n "hello é" | not @calcc --verbose -k -S --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_B // CHECK_A: Processing input 'Stdin' // CHECK_A: Read 7 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_number.calc b/tests/lit-tests/lex_number.calc similarity index 79% rename from tests/lit-llvm/lex_number.calc rename to tests/lit-tests/lex_number.calc index 67c3f61..79ff9df 100644 --- a/tests/lit-llvm/lex_number.calc +++ b/tests/lit-tests/lex_number.calc @@ -1,5 +1,5 @@ -// RUN: @calcc --verbose --lex -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --lex -e 10002 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose --lex -k -S -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose --lex -k -S -e 10002 2>&1 | @filecheck %s --check-prefix=CHECK_B // CHECK_A: Processing input 'Expression:256' // CHECK_A: Read 3 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_number_enhanced.calc b/tests/lit-tests/lex_number_enhanced.calc similarity index 77% rename from tests/lit-llvm/lex_number_enhanced.calc rename to tests/lit-tests/lex_number_enhanced.calc index 771ed62..9abb995 100644 --- a/tests/lit-llvm/lex_number_enhanced.calc +++ b/tests/lit-tests/lex_number_enhanced.calc @@ -1,10 +1,10 @@ -// RUN: @calcc --verbose --lex -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --lex -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc --verbose --lex -e 0xAF 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc --verbose --lex -e 0x0c1 2>&1 | @filecheck %s --check-prefix=CHECK_D -// RUN: @calcc --verbose --lex -e 012 2>&1 | @filecheck %s --check-prefix=CHECK_E -// RUN: not @calcc --verbose --lex -e 0x0z3 2>&1 | @filecheck %s --check-prefix=CHECK_F -// RUN: not @calcc --verbose --lex -e 140_3 2>&1 | @filecheck %s --check-prefix=CHECK_G +// RUN: @calcc --verbose -k -S --lex -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --lex -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose -k -S --lex -e 0xAF 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose -k -S --lex -e 0x0c1 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: @calcc --verbose -k -S --lex -e 012 2>&1 | @filecheck %s --check-prefix=CHECK_E +// RUN: not @calcc --verbose -k -S --lex -e 0x0z3 2>&1 | @filecheck %s --check-prefix=CHECK_F +// RUN: not @calcc --verbose -k -S --lex -e 140_3 2>&1 | @filecheck %s --check-prefix=CHECK_G // CHECK_A: Processing input 'Expression:-9' // CHECK_A: Read 2 bytes from buffer at line 0 diff --git a/tests/lit-rust/lex_other.calc b/tests/lit-tests/lex_other.calc similarity index 90% rename from tests/lit-rust/lex_other.calc rename to tests/lit-tests/lex_other.calc index 92975eb..8b3f846 100644 --- a/tests/lit-rust/lex_other.calc +++ b/tests/lit-tests/lex_other.calc @@ -1,4 +1,4 @@ -// RUN: @calcc --verbose --lex -e ',:-()/*' +// RUN: @calcc --verbose --lex -k -S -e ',:-()/*' 2>&1 | @filecheck %s // CHECK: Processing input 'Expression:,:-()/*' // CHECK: Read 7 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_stdin.calc b/tests/lit-tests/lex_stdin.calc similarity index 82% rename from tests/lit-llvm/lex_stdin.calc rename to tests/lit-tests/lex_stdin.calc index a985ccc..efbf6bb 100644 --- a/tests/lit-llvm/lex_stdin.calc +++ b/tests/lit-tests/lex_stdin.calc @@ -1,4 +1,4 @@ -// RUN: echo -n "hello" | @calcc --verbose --lex - 2>&1 | @filecheck %s +// RUN: echo -n "hello" | @calcc --verbose -k -S --lex - 2>&1 | @filecheck %s // CHECK: Processing input 'Stdin' // CHECK: Read 5 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_trailing_whitespace.calc b/tests/lit-tests/lex_trailing_whitespace.calc similarity index 90% rename from tests/lit-llvm/lex_trailing_whitespace.calc rename to tests/lit-tests/lex_trailing_whitespace.calc index 49c8ad1..d3d6241 100644 --- a/tests/lit-llvm/lex_trailing_whitespace.calc +++ b/tests/lit-tests/lex_trailing_whitespace.calc @@ -1,5 +1,5 @@ -// RUN: @calcc -v --lex -e "10 + 5 " 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc -v --lex -e "with: a: 3 + a " 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc -v --lex -k -S -e "10 + 5 " 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc -v --lex -k -S -e "with: a: 3 + a " 2>&1 | @filecheck %s --check-prefix=CHECK_B // CHECK_A: Processing input 'Expression:10 + 5 ' // CHECK_A: Read 8 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lex_withdecl.calc b/tests/lit-tests/lex_withdecl.calc similarity index 91% rename from tests/lit-llvm/lex_withdecl.calc rename to tests/lit-tests/lex_withdecl.calc index a3d3b11..2f4cbf0 100644 --- a/tests/lit-llvm/lex_withdecl.calc +++ b/tests/lit-tests/lex_withdecl.calc @@ -1,6 +1,6 @@ -// RUN: @calcc --verbose --lex -e "with: a,b:a+b" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --lex -e "with : a , b: a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc --verbose --lex -e "with: a,b:a*a/b" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose --lex -k -S -e "with: a,b:a+b" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose --lex -k -S -e "with : a , b: a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose --lex -k -S -e "with: a,b:a*a/b" 2>&1 | @filecheck %s --check-prefix=CHECK_C // CHECK_A: Processing input 'Expression:with: a,b:a+b' // CHECK_A: Read 13 bytes from buffer at line 0 diff --git a/tests/lit-llvm/lit.cfg b/tests/lit-tests/lit.cfg similarity index 86% rename from tests/lit-llvm/lit.cfg rename to tests/lit-tests/lit.cfg index 7d875f5..59c963a 100644 --- a/tests/lit-llvm/lit.cfg +++ b/tests/lit-tests/lit.cfg @@ -25,6 +25,10 @@ if "OS_NAME" in lit_config.params: config.available_features.add("OS_MACOS") elif os_name == "windows": config.available_features.add("OS_WINDOWS") + else: + config.available_features.add("OS_UNKNOWN") +else: + config.available_features.add("OS_UNKNOWN") if "ARCH" in lit_config.params: arch = lit_config.params["ARCH"] @@ -34,6 +38,12 @@ if "ARCH" in lit_config.params: config.available_features.add("ARCH_X86_64") elif arch == "aarch64": config.available_features.add("ARCH_AARCH64") + else: + config.available_features.add("ARCH_UNKNOWN") +else: + config.available_features.add("ARCH_UNKNOWN") + +print(f'Features configured: {", ".join([f for f in config.available_features])}\n') config.test_format = lit.formats.ShTest(False) config.test_source_root = os.path.dirname(__file__) diff --git a/tests/lit-llvm/maingen1.calc b/tests/lit-tests/maingen1.calc similarity index 98% rename from tests/lit-llvm/maingen1.calc rename to tests/lit-tests/maingen1.calc index 5f64b6c..bc08245 100644 --- a/tests/lit-llvm/maingen1.calc +++ b/tests/lit-tests/maingen1.calc @@ -1,8 +1,10 @@ // RUN: @calcc -O0 --llvmir -e "with: a,b: a+b + 10" | @filecheck %s // RUN: @calcc -O0 -S -e "with: a,b: a+b + 10" | @filecheck %s +// UNSUPPORTED: OS_MACOS + // CHECK-LABEL: ; ModuleID = 'main' -// CHECK: source_filename = "main" +// CHECK: source_filename = "-" // CHECK: @stderr = external global ptr, align 8 // CHECK: @.str.argerr = private unnamed_addr constant [50 x i8] c"Invalid number of args to main. Expected %d args\0A\00", align 1 diff --git a/tests/lit-llvm/maingen2.calc b/tests/lit-tests/maingen2.calc similarity index 97% rename from tests/lit-llvm/maingen2.calc rename to tests/lit-tests/maingen2.calc index 2382db4..9ebb9f7 100644 --- a/tests/lit-llvm/maingen2.calc +++ b/tests/lit-tests/maingen2.calc @@ -1,8 +1,10 @@ // RUN: @calcc -O0 --llvmir -e "1024*32/8" | @filecheck %s // RUN: @calcc -O0 -S -e "1024*32/8" | @filecheck %s +// UNSUPPORTED: OS_MACOS + // CHECK-LABEL: ; ModuleID = 'main' -// CHECK: source_filename = "main" +// CHECK: source_filename = "-" // CHECK: @stderr = external global ptr, align 8 // CHECK: @.str.argerr = private unnamed_addr constant [50 x i8] c"Invalid number of args to main. Expected %d args\0A\00", align 1 diff --git a/tests/lit-tests/option_c_main.calc b/tests/lit-tests/option_c_main.calc new file mode 100644 index 0000000..7f9ffbf --- /dev/null +++ b/tests/lit-tests/option_c_main.calc @@ -0,0 +1,60 @@ +// RUN: not @calcc --llvmir %s --no-main --c-main 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: not @calcc --llvmir %s --c-main --no-main 2>&1 | @filecheck %s --check-prefix=CHECK_A + +// RUN: @calcc -v --ir %s --c-main 2>&1 | @filecheck %s --check-prefix=CHECK_B + +// RUN: @calcc --llvmir %s -O0 --c-main 2>&1 | @filecheck %s --check-prefix=CHECK_C + +// CHECK_A: Incompatible compiler flags: '-k|--no-main' and '-C|--c-main' + +// CHECK_B: #include +// CHECK_B: #include + +// CHECK_B: #define BASE 1 +// CHECK_B: #define NUM_ARGS 2 +// CHECK_B: #define USAGE " , \n" + +// CHECK_B: typedef long long t_i64; +// CHECK_B: extern t_i64 calcc_main(i64, i64); + +// CHECK_B: int main(int argc, char **argv) { +// CHECK_B: if (argc != BASE + NUM_ARGS) { +// CHECK_B: (void) fprintf(stderr, "Invalid number of args to main. Expected %d args\n", NUM_ARGS); +// CHECK_B: (void) fprintf(stderr, USAGE); +// CHECK_B: return 1; +// CHECK_B: } + +// CHECK_B: /* Parameter declaration section: */ +// CHECK_B: const i64 p0 = (i64)atoll(argv[BASE + 0]); +// CHECK_B: const i64 p1 = (i64)atoll(argv[BASE + 1]); + +// CHECK_B: /* Function call section: */ +// CHECK_B: const t_i64 result = calcc_main(p0, p1); +// CHECK_B: (void) printf("calcc_main result: %lld\n", result); + +// CHECK_B: return 0; +// CHECK_B: } + +// CHECK_C-LABEL: ; ModuleID = 'calcc' + +// CHECK_C-LABEL: define i64 @calcc_main(i64 %0, i64 %1) { +// CHECK_C-LABEL: entry: +// CHECK_C: %a = alloca i64, align 8 +// CHECK_C: store i64 %0, ptr %a, align 4 +// CHECK_C: %b = alloca i64, align 8 +// CHECK_C: store i64 %1, ptr %b, align 4 +// CHECK_C: %v0 = load i64, ptr %a, align 4 +// CHECK_C: %v1 = load i64, ptr %a, align 4 +// CHECK_C: %v2 = mul nsw i64 %v0, %v1 +// CHECK_C: %v3 = load i64, ptr %b, align 4 +// CHECK_C: %v4 = mul nsw i64 2, %v3 +// CHECK_C: %v5 = add nsw i64 %v2, %v4 +// CHECK_C: %v6 = sub nsw i64 %v5, 3 +// CHECK_C: ret i64 %v6 +// CHECK_C: } + +// COM: CHECK_C-LABEL: i32 define @main(i32 %vArgc, ptr %vArgv) +// COM: CHECK_C-LABEL: entry: +// COM: CHECK_C: %[[REG0:.+]] = call i64 @calcc_main(i64 %[[REG1:.+]], i64 %[[REG2:.+]]) + +with: a,b: a*a + 2*b - 3 diff --git a/tests/lit-llvm/option_notarget.calc b/tests/lit-tests/option_notarget.calc similarity index 69% rename from tests/lit-llvm/option_notarget.calc rename to tests/lit-tests/option_notarget.calc index 4d72ae4..f2e9fed 100644 --- a/tests/lit-llvm/option_notarget.calc +++ b/tests/lit-tests/option_notarget.calc @@ -1,8 +1,8 @@ -// RUN: @calcc --nomain -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_TARGET -// RUN: @calcc --nomain --notarget -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_NOTARGET +// RUN: @calcc --no-main -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_TARGET +// RUN: @calcc --no-main --notarget -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_NOTARGET // CHECK_TARGET-LABEL: ; ModuleID = 'calcc' -// CHECK_TARGET: source_filename = "calcc" +// CHECK_TARGET: source_filename = "-" // CHECK_TARGET: target datalayout = "{{[a-zA-Z0-9\:\-]+}}" // CHECK_TARGET: target triple = "{{[_\.a-zA-Z0-9\-]+}}" @@ -12,7 +12,7 @@ // CHECK_TARGET: } // CHECK_NOTARGET-LABEL: ; ModuleID = 'calcc' -// CHECK_NOTARGET: source_filename = "calcc" +// CHECK_NOTARGET: source_filename = "-" // CHECK_NOTARGET-NOT: target datalayout = "{{[a-zA-Z0-9\:\-]+}}" // CHECK_NOTARGET-NOT: target triple = "{{[_\.a-zA-Z0-9\-]+}}" diff --git a/tests/lit-llvm/option_opt.calc b/tests/lit-tests/option_opt.calc similarity index 60% rename from tests/lit-llvm/option_opt.calc rename to tests/lit-tests/option_opt.calc index 862491f..f03270c 100644 --- a/tests/lit-llvm/option_opt.calc +++ b/tests/lit-tests/option_opt.calc @@ -1,8 +1,9 @@ -// RUN: @calcc --nomain -O0 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O0 -// RUN: @calcc --nomain -O1 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O1 +// RUN: @calcc --no-main -O0 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O0 +// RUN: @calcc --no-main -O1 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O1 +// RUN: @calcc --no-main -O1 --llvmir --ir -e "with: a: a*32" 2>&1 | @filecheck %s --check-prefix=CHECK_O0 // CHECK_O0-LABEL: ; ModuleID = 'calcc' -// CHECK_O0: source_filename = "calcc" +// CHECK_O0: source_filename = "-" // CHECK_O0-LABEL: define i64 @calcc_main(i64 %0) { // CHECK_O0-LABEL: entry: @@ -14,7 +15,7 @@ // CHECK_O0: } // CHECK_O1-LABEL: ; ModuleID = 'calcc' -// CHECK_O1: source_filename = "calcc" +// CHECK_O1: source_filename = "-" // CHECK_O1-LABEL: define i64 @calcc_main(i64 %0) {{.*}} { // CHECK_O1-LABEL: entry: diff --git a/tests/lit-tests/parse_eoi.calc b/tests/lit-tests/parse_eoi.calc new file mode 100644 index 0000000..2795e5a --- /dev/null +++ b/tests/lit-tests/parse_eoi.calc @@ -0,0 +1,21 @@ +// RUN: not @calcc -v -k -S --parse -e "" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: not @calcc -v -k -S --parse -e "25 + " 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: not @calcc -v -k -S --parse -e "with: a" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: not @calcc -v -k -S --parse -e "with: a: a +" 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: not @calcc -v -k -S --parse -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_E + +// CHECK_A: Lexed token 'Eoi:' +// CHECK_A: Unexpected token + +// CHECK_B: Lexed token 'Eoi:' +// CHECK_B: Unexpected token + +// CHECK_C: Lexed token 'Eoi:' +// CHECK_C: Expected 'Colon' token at position 3 + +// CHECK_D: Lexed token 'Eoi:' +// CHECK_D: Unexpected token + +// CHECK_E: Lexed token 'Comment:// comment' +// CHECK_E: Lexed token 'Eoi:' +// CHECK_E: Unexpected token diff --git a/tests/lit-llvm/parse_expr_arith.calc b/tests/lit-tests/parse_expr_arith.calc similarity index 76% rename from tests/lit-llvm/parse_expr_arith.calc rename to tests/lit-tests/parse_expr_arith.calc index 940f545..993401a 100644 --- a/tests/lit-llvm/parse_expr_arith.calc +++ b/tests/lit-tests/parse_expr_arith.calc @@ -1,9 +1,9 @@ -// RUN: @calcc --verbose --ast --parse -e "32 + 50" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --ast --parse -e "32 - 50" 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc --verbose --ast --parse -e "32 / 50" 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc --verbose --ast --parse -e "32 * 50" 2>&1 | @filecheck %s --check-prefix=CHECK_D -// RUN: @calcc --verbose --ast --parse -e "32*2/4 + 50/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_E -// RUN: @calcc --verbose --ast --parse -e "32*-2/4 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_F +// RUN: @calcc --verbose -k -S --ast --parse -e "32 + 50" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --ast --parse -e "32 - 50" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose -k -S --ast --parse -e "32 / 50" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose -k -S --ast --parse -e "32 * 50" 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: @calcc --verbose -k -S --ast --parse -e "32*2/4 + 50/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc --verbose -k -S --ast --parse -e "32*-2/4 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_F // CHECK_A: Consumed expected token 'Number' at position '0' // CHECK_A: Consumed expected token 'Plus' at position '1' diff --git a/tests/lit-llvm/parse_expr_unary_minus.calc b/tests/lit-tests/parse_expr_unary_minus.calc similarity index 76% rename from tests/lit-llvm/parse_expr_unary_minus.calc rename to tests/lit-tests/parse_expr_unary_minus.calc index ecfeabd..0a1b162 100644 --- a/tests/lit-llvm/parse_expr_unary_minus.calc +++ b/tests/lit-tests/parse_expr_unary_minus.calc @@ -1,10 +1,10 @@ -// RUN: @calcc --verbose --ast --parse -e "0 - N" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --ast --parse -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: @calcc --verbose --ast --parse -e "-N + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: @calcc --verbose --ast --parse -e "2 + -N" 2>&1 | @filecheck %s --check-prefix=CHECK_D -// RUN: @calcc --verbose --ast --parse -e "-(N) + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_E -// RUN: @calcc --verbose --ast --parse -e "2 + -(N)" 2>&1 | @filecheck %s --check-prefix=CHECK_F -// RUN: @calcc --verbose --ast --parse -e "2 + -(N*2)" 2>&1 | @filecheck %s --check-prefix=CHECK_G +// RUN: @calcc --verbose -k -S --ast --parse -e "0 - N" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --ast --parse -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose -k -S --ast --parse -e "-N + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose -k -S --ast --parse -e "2 + -N" 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: @calcc --verbose -k -S --ast --parse -e "-(N) + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc --verbose -k -S --ast --parse -e "2 + -(N)" 2>&1 | @filecheck %s --check-prefix=CHECK_F +// RUN: @calcc --verbose -k -S --ast --parse -e "2 + -(N*2)" 2>&1 | @filecheck %s --check-prefix=CHECK_G // CHECK_A: Consumed expected token 'Number' at position '0' // CHECK_A: Consumed expected token 'Minus' at position '1' diff --git a/tests/lit-llvm/parse_expr_withdecl.calc b/tests/lit-tests/parse_expr_withdecl.calc similarity index 89% rename from tests/lit-llvm/parse_expr_withdecl.calc rename to tests/lit-tests/parse_expr_withdecl.calc index e149194..8df6723 100644 --- a/tests/lit-llvm/parse_expr_withdecl.calc +++ b/tests/lit-tests/parse_expr_withdecl.calc @@ -1,5 +1,5 @@ -// RUN: @calcc --verbose --ast --parse -e "with: a: 32*a/4 + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --ast --parse -e "with: a,b: 32*a/b + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose -k -S --ast --parse -e "with: a: 32*a/4 + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --ast --parse -e "with: a,b: 32*a/b + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_B // CHECK_A: Consumed expected token 'With' at position '0' // CHECK_A: Consumed expected token 'Colon' at position '1' diff --git a/tests/lit-tests/parse_idents.calc b/tests/lit-tests/parse_idents.calc new file mode 100644 index 0000000..fdae538 --- /dev/null +++ b/tests/lit-tests/parse_idents.calc @@ -0,0 +1,20 @@ +// RUN: @calcc --verbose -k -S --ast --parse -e a 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --ast --parse -e point 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose -k -S --ast --parse -e Zpos 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose -k -S --ast --parse -e __file__ 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: @calcc --verbose -k -S --ast --parse -e tmp002 2>&1 | @filecheck %s --check-prefix=CHECK_E + +// CHECK_A: Consumed expected token 'Ident' at position '0' +// CHECK_A: AST: Ident(a) + +// CHECK_B: Consumed expected token 'Ident' at position '0' +// CHECK_B: AST: Ident(point) + +// CHECK_C: Consumed expected token 'Ident' at position '0' +// CHECK_C: AST: Ident(Zpos) + +// CHECK_D: Consumed expected token 'Ident' at position '0' +// CHECK_D: AST: Ident(__file__) + +// CHECK_E: Consumed expected token 'Ident' at position '0' +// CHECK_E: AST: Ident(tmp002) diff --git a/tests/lit-tests/parse_numbers.calc b/tests/lit-tests/parse_numbers.calc new file mode 100644 index 0000000..1ee3fc9 --- /dev/null +++ b/tests/lit-tests/parse_numbers.calc @@ -0,0 +1,31 @@ +// RUN: @calcc --verbose -k -S --ast --parse -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --ast --parse -e 9 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: @calcc --verbose -k -S --ast --parse -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose -k -S --ast --parse -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: @calcc --verbose -k -S --ast --parse -e "0x7FFFFFFFFFFFFFFF" 2>&1 | @filecheck %s --check-prefix=CHECK_D + +// RUN: not @calcc --verbose -k -S --parse -e "0x8000000000000000" 2>&1 | @filecheck %s --check-prefix=CHECK_E +// RUN: not @calcc --verbose -k -S --parse -e "9223372036854775808" 2>&1 | @filecheck %s --check-prefix=CHECK_F +// RUN: not @calcc --verbose -k -S --parse -e "-9223372036854775809" 2>&1 | @filecheck %s --check-prefix=CHECK_G + +// CHECK_A: Consumed expected token 'Number' at position '0' +// CHECK_A: AST: 256 + +// CHECK_B: Consumed expected token 'Number' at position '0' +// CHECK_B: AST: 9 + +// CHECK_C: Consumed expected token 'Minus' at position '0' +// CHECK_C: Consumed expected token 'Number' at position '1' +// CHECK_C: AST: -9 + +// CHECK_D: Consumed expected token 'Number' at position '0' +// CHECK_D: AST: 9223372036854775807 + +// CHECK_E: Number '0x8000000000000000' failed parse: number too large to fit in target type +// CHECK_E: Failed to convert hexadecimal string + +// CHECK_F: Number '9223372036854775808' failed parse: number too large to fit in target type +// CHECK_F: Failed to convert decimal string + +// CHECK_G: Number '-9223372036854775809' failed parse: number too small to fit in target type +// CHECK_G: Failed to convert decimal string diff --git a/tests/lit-llvm/sem_decl.calc b/tests/lit-tests/sem_decl.calc similarity index 56% rename from tests/lit-llvm/sem_decl.calc rename to tests/lit-tests/sem_decl.calc index 82406d3..72b0f23 100644 --- a/tests/lit-llvm/sem_decl.calc +++ b/tests/lit-tests/sem_decl.calc @@ -1,8 +1,8 @@ -// RUN: @calcc --verbose --sem -e "with: a,b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_A -// RUN: @calcc --verbose --sem -e "with: a,b: b + 2*a" 2>&1 | @filecheck %s --check-prefix=CHECK_B -// RUN: not @calcc --verbose --sem -e "with: a: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_C -// RUN: not @calcc --verbose --sem -e "with: b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_D -// RUN: not @calcc --verbose --sem -e "with: b,b: 2*b + b" 2>&1 | @filecheck %s --check-prefix=CHECK_E +// RUN: @calcc --verbose -k -S --sem -e "with: a,b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_A +// RUN: @calcc --verbose -k -S --sem -e "with: a,b: b + 2*a" 2>&1 | @filecheck %s --check-prefix=CHECK_B +// RUN: not @calcc --verbose -k -S --sem -e "with: a: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_C +// RUN: not @calcc --verbose -k -S --sem -e "with: b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_D +// RUN: not @calcc --verbose -k -S --sem -e "with: b,b: 2*b + b" 2>&1 | @filecheck %s --check-prefix=CHECK_E // CHECK_A: Added var 'a' to scope // CHECK_A: Added var 'b' to scope diff --git a/tests/lit-llvm/usage.calc b/tests/lit-tests/usage.calc similarity index 100% rename from tests/lit-llvm/usage.calc rename to tests/lit-tests/usage.calc diff --git a/tests/lit-llvm/version.calc b/tests/lit-tests/version.calc similarity index 75% rename from tests/lit-llvm/version.calc rename to tests/lit-tests/version.calc index b831d89..255ee6e 100644 --- a/tests/lit-llvm/version.calc +++ b/tests/lit-tests/version.calc @@ -1,5 +1,5 @@ // RUN: @calcc --version 2>&1 | @filecheck %s -// CHECK: Welcome to calcc version v0.1.0-dev +// CHECK: Welcome to calcc version v0.2.0 // CHECK: // Copyright 2024, Giordano Salvador // CHECK: // SPDX-License-Identifier: BSD-3-Clause