Browse Source

QBE generator (#42)

* Only build standard library for JS target

* Add initial data structures for QBE generator

* Allow generators to return an error

Required for QBE generator

* qbe: implement function generation

* qbe: generate integer literal expression

* qbe: generate return with expression

* qbe: generate boolean expression

* qbe: implement most of binary operations

* qbe: store a type in scope maps

* qbe: implement variable access expression

* qbe: implement function declarations

* qbe: implement if/else

* qbe: implement while loops

* qbe: implement break in loops

* qbe: implement continue

* qbe: implement expression statement

* qbe: implement function calls

* qbe: implement assignment for variables

* qbe: add QbeValue, use it instead of temporaries

With old approach, it was not possible for function call instruction to
accept a $-global value (for static strings), as well as having an
inconsistent API for QbeInstr::Copy because of Either<...>.

QbeValue can hold either a temporary, global or a constant literal. This
allows it to be used everywhere, without hacks like Either for copy.

* qbe: implement string literals

* qbe: move Assign into generate_assignment

* qbe: add typedefs

* qbe: generate offset table for structs

* qbe: implement struct initialization

* qbe: resolve struct variable types

* qbe: convert assignment QBE statements to base types

This prevents generating incorrect IR with structs

* qbe: implement field access

* qbe: do not cast to ABI types in scope

* qbe: implement assignments to field access

* qbe: store loop labels as a stack

Fixes issues when using continue/break in nested loops.

* docs: add QBE to supported backends

* Add QBE backend to changelog

* qbe: implement assignment BinOps (e.g. +=)

* qbe: resolve arrays in variable types

* qbe: implement array initialization

* qbe: add aggregate type fills (e.g. { w 12 })

* qbe: generate aggregate types for arrays
traversable-ast
Alexey 3 years ago committed by GitHub
parent
commit
35895865b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 5
      docs/developers/backends.md
  3. 15
      src/builder/mod.rs
  4. 2
      src/command/build.rs
  5. 9
      src/generator/c.rs
  6. 6
      src/generator/js.rs
  7. 8
      src/generator/mod.rs
  8. 1337
      src/generator/qbe.rs
  9. 8
      src/generator/x86.rs
  10. 1
      src/util/mod.rs

1
CHANGELOG.md

@ -14,6 +14,7 @@
- Parser errors have been improved in consistency and readability
- Compile to stdout by using the `-o -` flag
- Proper support for utf-8
- Initial support for QBE backend
**Fixes**

5
docs/developers/backends.md

@ -1,6 +1,6 @@
# Backends
Antimony currently implements a JavaScript backend, but a C backend is in development. WASM, ARM and x86 are planned.
Antimony currently implements a JavaScript backend, but C and QBE backends are in development. WASM, ARM and x86 are planned.
Backend can be specified when running on building with `--target` (`-t`) option, default is `js`:
@ -13,9 +13,12 @@ sb -t c build in.sb --out-file out
| Target Language | Identifier | Stability notice |
| :-------------- | :------------- | :--------------- |
| Node.js | `js` | mostly stable |
| [QBE] | `qbe` | work in progess |
| LLVM | `llvm` | unstable |
| C | `c` | unstable |
[QBE]: https://c9x.me/compile
LLVM also requires to enable `llvm` feature when building:
```sh

15
src/builder/mod.rs

@ -45,7 +45,7 @@ impl Builder {
.to_path_buf())
}
pub fn build(&mut self) -> Result<(), String> {
pub fn build(&mut self, target: &Target) -> Result<(), String> {
let in_file = self.in_file.clone();
// Resolve path deltas between working directory and entrypoint
let base_directory = self.get_base_path()?;
@ -61,7 +61,9 @@ impl Builder {
self.build_module(self.in_file.clone(), &mut Vec::new())?;
// Append standard library
self.build_stdlib()?;
if matches!(target, Target::JS) {
self.build_stdlib()?;
}
// Change back to the initial directory
env::set_current_dir(initial_directory).expect("Could not set current directory");
@ -132,16 +134,17 @@ impl Builder {
}
let output = match target {
Target::JS => generator::js::JsGenerator::generate(condensed),
Target::C => generator::c::CGenerator::generate(condensed),
Target::JS => generator::js::JsGenerator::generate(condensed)?,
Target::C => generator::c::CGenerator::generate(condensed)?,
Target::Llvm => {
#[cfg(not(feature = "llvm"))]
panic!("'llvm' feature should be enabled to use LLVM target");
#[cfg(feature = "llvm")]
generator::llvm::LLVMGenerator::generate(condensed)
generator::llvm::LLVMGenerator::generate(condensed)?
}
Target::X86 => generator::x86::X86Generator::generate(condensed),
Target::Qbe => generator::qbe::QbeGenerator::generate(condensed)?,
Target::X86 => generator::x86::X86Generator::generate(condensed)?,
};
buffer.write_all(output.as_bytes()).expect("write failed");

2
src/command/build.rs

@ -42,6 +42,6 @@ pub fn build_to_buffer(
buf: &mut Box<impl Write>,
) -> Result<(), String> {
let mut b = builder::Builder::new(in_file.to_path_buf());
b.build()?;
b.build(target)?;
b.generate(target, buf)
}

9
src/generator/c.rs

@ -13,17 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::collections::HashMap;
use crate::ast::types::Type;
use crate::ast::*;
use crate::generator::Generator;
use crate::generator::{Generator, GeneratorResult};
use crate::util::Either;
use std::collections::HashMap;
pub struct CGenerator;
impl Generator for CGenerator {
fn generate(prog: Module) -> String {
fn generate(prog: Module) -> GeneratorResult<String> {
let mut code = String::new();
let raw_builtins =
@ -43,7 +42,7 @@ impl Generator for CGenerator {
code += &funcs;
code
Ok(code)
}
}

6
src/generator/js.rs

@ -14,14 +14,14 @@
* limitations under the License.
*/
use crate::ast::*;
use crate::generator::Generator;
use crate::generator::{Generator, GeneratorResult};
use std::collections::HashMap;
use types::Type;
pub struct JsGenerator;
impl Generator for JsGenerator {
fn generate(prog: Module) -> String {
fn generate(prog: Module) -> GeneratorResult<String> {
let mut code = String::new();
let raw_builtins =
@ -44,7 +44,7 @@ impl Generator for JsGenerator {
code += "main();";
code
Ok(code)
}
}

8
src/generator/mod.rs

@ -21,6 +21,7 @@ pub mod c;
pub mod js;
#[cfg(feature = "llvm")]
pub mod llvm;
pub mod qbe;
#[cfg(test)]
mod tests;
pub mod x86;
@ -30,6 +31,7 @@ pub enum Target {
C,
JS,
Llvm,
Qbe,
X86,
}
@ -42,6 +44,7 @@ impl Target {
match &*ext.to_string_lossy() {
"c" => Some(Self::C),
"js" => Some(Self::JS),
"ssa" => Some(Self::Qbe),
"s" => Some(Self::X86),
_ => None,
}
@ -58,14 +61,17 @@ impl FromStr for Target {
"c" => Ok(Target::C),
"js" => Ok(Target::JS),
"llvm" => Ok(Target::Llvm),
"qbe" => Ok(Target::Qbe),
"x86" => Ok(Target::X86),
_ => Err(format!("no target {} found", s)),
}
}
}
pub type GeneratorResult<T> = Result<T, String>;
pub trait Generator {
fn generate(prog: Module) -> String;
fn generate(prog: Module) -> GeneratorResult<String>;
}
/// Returns C syntax representation of a raw string

1337
src/generator/qbe.rs

File diff suppressed because it is too large Load Diff

8
src/generator/x86.rs

@ -1,4 +1,3 @@
use crate::ast::{Function, Module, Statement};
/**
* Copyright 2020 Garrit Franke
*
@ -14,7 +13,8 @@ use crate::ast::{Function, Module, Statement};
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::generator::Generator;
use crate::ast::{Function, Module, Statement};
use crate::generator::{Generator, GeneratorResult};
struct Assembly {
asm: Vec<String>,
@ -43,8 +43,8 @@ impl Assembly {
pub struct X86Generator;
impl Generator for X86Generator {
fn generate(prog: Module) -> String {
Self::new().gen_program(prog).build()
fn generate(prog: Module) -> GeneratorResult<String> {
Ok(Self::new().gen_program(prog).build())
}
}

1
src/util/mod.rs

@ -16,6 +16,7 @@
pub mod string_util;
/// Datatype that holds one of two types
#[derive(Debug)]
pub enum Either<L, R> {
Left(L),
Right(R),

Loading…
Cancel
Save