Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/doom-demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ Useful global options:

---

## Contributing

Contributions are welcome! Please read the [contributing guidelines](CONTRIBUTING.md) before submitting a pull request.

---

## What can do?

### [Doom Domo](https://github.com/wavefnd/Wave/tree/master/examples/doom.wave)

[doom-demo](.github/doom-demo.gif)

---

<p align="center">
<a href="https://star-history.com/#wavefnd/Wave&Date">
<img src="https://api.star-history.com/svg?repos=wavefnd/Wave&type=Date" alt="Star History Chart" width="80%">
Expand Down
119 changes: 103 additions & 16 deletions front/parser/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,98 @@ fn is_supported_target_item_start(line: &str) -> bool {
false
}

fn consume_target_item(
lines: &[&str],
mut idx: usize,
keep: bool,
out: &mut Vec<String>,
) -> usize {
fn scan_target_item_line(
line: &str,
in_block_comment: &mut bool,
depth: &mut i32,
seen_open: &mut bool,
saw_semicolon: &mut bool,
) {
let mut chars = line.chars().peekable();
let mut in_string = false;
let mut in_char = false;
let mut escape = false;

while let Some(ch) = chars.next() {
if *in_block_comment {
if ch == '*' && chars.peek() == Some(&'/') {
chars.next();
*in_block_comment = false;
}
continue;
}

if in_string {
if escape {
escape = false;
continue;
}
if ch == '\\' {
escape = true;
continue;
}
if ch == '"' {
in_string = false;
}
continue;
}

if in_char {
if escape {
escape = false;
continue;
}
if ch == '\\' {
escape = true;
continue;
}
if ch == '\'' {
in_char = false;
}
continue;
}

if ch == '/' {
if chars.peek() == Some(&'/') {
break;
}
if chars.peek() == Some(&'*') {
chars.next();
*in_block_comment = true;
continue;
}
}

if ch == '"' {
in_string = true;
continue;
}
if ch == '\'' {
in_char = true;
continue;
}

if ch == '{' {
*depth += 1;
*seen_open = true;
continue;
}
if ch == '}' {
if *depth > 0 {
*depth -= 1;
}
continue;
}
if ch == ';' {
*saw_semicolon = true;
}
}
}

fn consume_target_item(lines: &[&str], mut idx: usize, keep: bool, out: &mut Vec<String>) -> usize {
let mut depth: i32 = 0;
let mut seen_open = false;
let mut in_block_comment = false;

while idx < lines.len() {
let line = lines[idx];
Expand All @@ -74,19 +158,22 @@ fn consume_target_item(
out.push(String::new());
}

for ch in line.chars() {
if ch == '{' {
depth += 1;
seen_open = true;
} else if ch == '}' && depth > 0 {
depth -= 1;
}
}
let mut saw_semicolon = false;
scan_target_item_line(
line,
&mut in_block_comment,
&mut depth,
&mut seen_open,
&mut saw_semicolon,
);

idx += 1;

let trimmed = line.trim_end();
if seen_open && depth == 0 {
if seen_open {
if depth == 0 {
break;
}
} else if saw_semicolon {
break;
}
}
Expand Down
66 changes: 60 additions & 6 deletions llvm/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@
//
// SPDX-License-Identifier: MPL-2.0

use std::fs;
use std::path::Path;
use std::process::Command;

#[derive(Debug, Default, Clone)]
pub struct BackendOptions {
pub target: Option<String>,
pub cpu: Option<String>,
pub features: Option<String>,
pub abi: Option<String>,
pub sysroot: Option<String>,
pub linker: Option<String>,
pub link_args: Vec<String>,
pub no_default_libs: bool,
}

fn normalize_clang_opt_flag(opt_flag: &str) -> &str {
match opt_flag {
// LLVM pass pipeline currently has no dedicated Ofast preset, so keep
Expand All @@ -22,12 +32,29 @@ fn normalize_clang_opt_flag(opt_flag: &str) -> &str {
}
}

pub fn compile_ir_to_object(ir: &str, file_stem: &str, opt_flag: &str) -> String {
pub fn compile_ir_to_object(
ir: &str,
file_stem: &str,
opt_flag: &str,
backend: &BackendOptions,
) -> String {
let object_path = format!("{}.o", file_stem);

let normalized_opt = normalize_clang_opt_flag(opt_flag);
let mut cmd = Command::new("clang");

if let Some(target) = &backend.target {
cmd.arg(format!("--target={}", target));
}

if let Some(sysroot) = &backend.sysroot {
cmd.arg(format!("--sysroot={}", sysroot));
}

if let Some(abi) = &backend.abi {
cmd.arg("-target-abi").arg(abi);
}

if !normalized_opt.is_empty() {
cmd.arg(normalized_opt);
}
Expand Down Expand Up @@ -61,8 +88,27 @@ pub fn compile_ir_to_object(ir: &str, file_stem: &str, opt_flag: &str) -> String
object_path
}

pub fn link_objects(objects: &[String], output: &str, libs: &[String], lib_paths: &[String]) {
let mut cmd = Command::new("clang");
pub fn link_objects(
objects: &[String],
output: &str,
libs: &[String],
lib_paths: &[String],
backend: &BackendOptions,
) {
let linker_bin = backend.linker.as_deref().unwrap_or("clang");
let mut cmd = Command::new(linker_bin);

if backend.linker.is_none() {
if let Some(target) = &backend.target {
cmd.arg(format!("--target={}", target));
}
if let Some(sysroot) = &backend.sysroot {
cmd.arg(format!("--sysroot={}", sysroot));
}
if let Some(abi) = &backend.abi {
cmd.arg("-target-abi").arg(abi);
}
}

for obj in objects {
cmd.arg(obj);
Expand All @@ -76,7 +122,15 @@ pub fn link_objects(objects: &[String], output: &str, libs: &[String], lib_paths
cmd.arg(format!("-l{}", lib));
}

cmd.arg("-o").arg(output).arg("-lc").arg("-lm");
for arg in &backend.link_args {
cmd.arg(arg);
}

cmd.arg("-o").arg(output);

if !backend.no_default_libs {
cmd.arg("-lc").arg("-lm");
}

let output = cmd.output().expect("Failed to link");
if !output.status.success() {
Expand Down
14 changes: 10 additions & 4 deletions llvm/src/codegen/abi_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,12 @@ fn classify_param<'ctx>(
t: BasicTypeEnum<'ctx>,
) -> ParamLowering<'ctx> {
match target {
CodegenTarget::LinuxX86_64 => classify_param_x86_64_sysv(context, td, t),
CodegenTarget::DarwinArm64 => classify_param_arm64_darwin(td, t),
CodegenTarget::LinuxX86_64 | CodegenTarget::DarwinX86_64 => {
classify_param_x86_64_sysv(context, td, t)
}
CodegenTarget::LinuxArm64 | CodegenTarget::DarwinArm64 => {
classify_param_arm64_darwin(td, t)
}
}
}

Expand All @@ -414,8 +418,10 @@ fn classify_ret<'ctx>(
t: Option<BasicTypeEnum<'ctx>>,
) -> RetLowering<'ctx> {
match target {
CodegenTarget::LinuxX86_64 => classify_ret_x86_64_sysv(context, td, t),
CodegenTarget::DarwinArm64 => classify_ret_arm64_darwin(td, t),
CodegenTarget::LinuxX86_64 | CodegenTarget::DarwinX86_64 => {
classify_ret_x86_64_sysv(context, td, t)
}
CodegenTarget::LinuxArm64 | CodegenTarget::DarwinArm64 => classify_ret_arm64_darwin(td, t),
}
}

Expand Down
35 changes: 28 additions & 7 deletions llvm/src/codegen/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue};
use inkwell::OptimizationLevel;

use inkwell::targets::{
CodeModel, InitializationConfig, RelocMode, Target, TargetData, TargetMachine,
CodeModel, InitializationConfig, RelocMode, Target, TargetData, TargetMachine, TargetTriple,
};
use parser::ast::{
ASTNode, EnumNode, ExternFunctionNode, FunctionNode, Mutability, ParameterNode, ProtoImplNode,
StructNode, TypeAliasNode, VariableNode, WaveType,
};
use std::collections::{HashMap, HashSet};

use crate::backend::BackendOptions;
use crate::codegen::target::require_supported_target_from_triple;
use crate::statement::generate_statement_ir;

Expand All @@ -48,7 +49,21 @@ fn normalize_opt_flag_for_passes(opt_flag: &str) -> &str {
}
}

pub unsafe fn generate_ir(ast_nodes: &[ASTNode], opt_flag: &str) -> String {
fn target_opt_level_from_flag(opt_flag: &str) -> OptimizationLevel {
match normalize_opt_flag_for_passes(opt_flag) {
"" | "-O0" => OptimizationLevel::None,
"-O1" => OptimizationLevel::Less,
"-O2" | "-Os" | "-Oz" => OptimizationLevel::Default,
"-O3" => OptimizationLevel::Aggressive,
other => panic!("unknown opt flag for target machine: {}", other),
}
}

pub unsafe fn generate_ir(
ast_nodes: &[ASTNode],
opt_flag: &str,
backend: &BackendOptions,
) -> String {
let context: &'static Context = Box::leak(Box::new(Context::create()));
let module: &'static _ = Box::leak(Box::new(context.create_module("main")));
let builder: &'static _ = Box::leak(Box::new(context.create_builder()));
Expand All @@ -59,17 +74,23 @@ pub unsafe fn generate_ir(ast_nodes: &[ASTNode], opt_flag: &str) -> String {
.map(|n| resolve_ast_node(n, &named_types))
.collect();

Target::initialize_native(&InitializationConfig::default()).unwrap();
let triple = TargetMachine::get_default_triple();
Target::initialize_all(&InitializationConfig::default());
let triple = if let Some(raw) = &backend.target {
TargetTriple::create(raw)
} else {
TargetMachine::get_default_triple()
};
let abi_target = require_supported_target_from_triple(&triple);
let target = Target::from_triple(&triple).unwrap();
let cpu = backend.cpu.as_deref().unwrap_or("generic");
let features = backend.features.as_deref().unwrap_or("");

let tm = target
.create_target_machine(
&triple,
"generic",
"",
OptimizationLevel::Default,
cpu,
features,
target_opt_level_from_flag(opt_flag),
RelocMode::Default,
CodeModel::Default,
)
Expand Down
Loading