@@ -1,17 +1,24 @@
11use std::cmp::max;
22use std::convert::TryInto;
334-use inkwell::context::Context;
5-use inkwell::types::{self, AnyType, AnyTypeEnum, BasicType, BasicTypeEnum};
6-use inkwell::AddressSpace;
4+use cranelift_codegen::ir::types::{self, Type as IrType};
5+use cranelift_codegen::ir::{AbiParam, Signature};
6+use cranelift_codegen::isa::CallConv;
7+use target_lexicon::Triple;
788-use crate::data::{Type, INT_POINTER};
9+use crate::data::{FunctionType, Locatable, Location, Type};
910use Type::*;
10111112// NOTE: this is required by the standard to always be one
12-const CHAR_SIZE: u32 = 1;
13+const CHAR_SIZE: u16 = 1;
13141415// TODO: allow this to be configured at runtime
16+lazy_static! {
17+// TODO: make this `const` when
18+// https://github.com/CraneStation/target-lexicon/pull/19 is merged
19+pub static ref TARGET: Triple = Triple::host();
20+pub static ref CALLING_CONVENTION: CallConv = CallConv::triple_default(&TARGET);
21+}
1522mod x64;
1623pub use x64::*;
1724@@ -23,18 +30,19 @@ impl Type {
2330 other
2431)
2532}
33+2634// TODO: instead of doing this manually,
2735// convert to LLVM type and call t.size_of()
28-pub fn sizeof(&self) -> Result<u32, &'static str> {
36+pub fn sizeof(&self) -> Result<SIZE_T, &'static str> {
2937match self {
30-Bool => Ok(BOOL_SIZE * CHAR_BIT),
31-Char(_) => Ok(CHAR_SIZE * CHAR_BIT),
32-Short(_) => Ok(SHORT_SIZE * CHAR_BIT),
33-Int(_) => Ok(INT_SIZE * CHAR_BIT),
34-Long(_) => Ok(LONG_SIZE * CHAR_BIT),
35-Float => Ok(FLOAT_SIZE * CHAR_BIT),
36-Double => Ok(DOUBLE_SIZE * CHAR_BIT),
37-Pointer(_, _) => Ok(PTR_SIZE * CHAR_BIT),
38+Bool => Ok(BOOL_SIZE.into()),
39+Char(_) => Ok(CHAR_SIZE.into()),
40+Short(_) => Ok(SHORT_SIZE.into()),
41+Int(_) => Ok(INT_SIZE.into()),
42+Long(_) => Ok(LONG_SIZE.into()),
43+Float => Ok(FLOAT_SIZE.into()),
44+Double => Ok(DOUBLE_SIZE.into()),
45+Pointer(_, _) => Ok(PTR_SIZE.into()),
3846// now for the hard ones
3947Array(t, l) => t.sizeof().and_then(|n| Ok(n * l.length()?)),
4048Enum(symbols) => {
@@ -66,7 +74,7 @@ impl Type {
6674}
6775// TODO: instead of doing this manually,
6876// convert to LLVM type and call t.size_of()
69-pub fn alignof(&self) -> Result<u32, &'static str> {
77+pub fn alignof(&self) -> Result<SIZE_T, &'static str> {
7078match self {
7179Bool
7280 | Char(_)
@@ -86,136 +94,52 @@ impl Type {
8694Void => Err("cannot take `alignof` void"),
8795}
8896}
89-}
90-91-// given an enum $enum with some variants that share a method,
92-// call that method on each of them
93-// useful if each variant of an enum has that method but the enum doesn't implement
94-// a trait giving you access to it
95-macro_rules! gen_calls {
96-// an enum to match and a method to call on all variants
97-( $enum: expr, $method: ident,
98-// with arbitrary arguments
99- $args: tt,
100-// for an arbitrary number of variants
101- $( $variant: path ),*
102-) => {
103-match $enum {
104- $( $variant(t) => t.$method($args), )*
105-}
106-}
107-}
108-109-trait ToPointerType {
110-fn ptr_type(&self, address_space: AddressSpace) -> types::PointerType;
111-}
112-trait ToArrayType {
113-fn array_type(&self, array_size: u32) -> types::ArrayType;
114-}
115-impl ToPointerType for BasicTypeEnum {
116-fn ptr_type(&self, addr: AddressSpace) -> types::PointerType {
117-use BasicTypeEnum::*;
118-gen_calls!(
119-self,
120- ptr_type,
121- addr,
122-FloatType,
123-IntType,
124-PointerType,
125-StructType,
126-VectorType,
127-ArrayType
128-)
129-}
130-}
131-impl ToPointerType for types::VoidType {
132-fn ptr_type(&self, addr: AddressSpace) -> types::PointerType {
133-self.get_context()
134-.custom_width_int_type(
135-INT_POINTER
136-.sizeof()
137-.expect("pointers should always have a valid size"),
138-)
139-.ptr_type(AddressSpace::Generic)
140-}
141-}
142-impl ToPointerType for AnyTypeEnum {
143-fn ptr_type(&self, addr: AddressSpace) -> types::PointerType {
144-use AnyTypeEnum::*;
145-gen_calls!(
146-self,
147- ptr_type,
148- addr,
149-FloatType,
150-IntType,
151-PointerType,
152-StructType,
153-VectorType,
154-ArrayType,
155-FunctionType,
156-VoidType
157-)
158-}
159-}
160-impl ToArrayType for BasicTypeEnum {
161-fn array_type(&self, array_size: u32) -> types::ArrayType {
162-use BasicTypeEnum::*;
163-gen_calls!(
164-self,
165- array_type,
166- array_size,
167-PointerType,
168-FloatType,
169-IntType,
170-StructType,
171-VectorType,
172-ArrayType
173-)
174-}
175-}
176-177-impl Type {
178-pub fn into_llvm_basic(self, context: &Context) -> Result<BasicTypeEnum, String> {
97+pub fn into_llvm_basic(self) -> Result<IrType, String> {
17998match self {
180-Bool | Char(_) | Short(_) | Int(_) | Long(_) | Enum(_) => Ok(context
181-.custom_width_int_type(self.sizeof()?)
182-.as_basic_type_enum()),
99+// Integers
100+Bool | Char(_) | Short(_) | Int(_) | Long(_) | Pointer(_, _) | Enum(_) => {
101+let int_size = SIZE_T::from(CHAR_BIT)
102+* self
103+.sizeof()
104+.expect("integers should always have a valid size");
105+Ok(IrType::int(int_size.try_into().unwrap_or_else(|_| {
106+panic!(
107+"integers should never have a size larger than {}",
108+i16::max_value()
109+)
110+}))
111+.unwrap_or_else(|| panic!("unsupported size for IR: {}", int_size)))
112+}
183113114+// Floats
184115// TODO: this is hard-coded for x64 because LLVM doesn't allow specifying a
185116// custom type
186-Float => Ok(context.f32_type().as_basic_type_enum()),
187-Double => Ok(context.f64_type().as_basic_type_enum()),
117+Float => Ok(types::F32),
118+Double => Ok(types::F64),
188119189-// derived types
190-Pointer(t, _) => Ok(t
191-.into_llvm(context)?
192-.ptr_type(AddressSpace::Generic)
193-.as_basic_type_enum()),
194-Array(t, l) => Ok(t
195-.into_llvm_basic(context)?
196-.array_type(l.length()?)
197-.as_basic_type_enum()),
120+// Aggregates
121+// arrays decay to pointers at the assembly level
122+Array(t, l) => Ok(IrType::int(PTR_SIZE * CHAR_BIT)
123+.unwrap_or_else(|| panic!("unsupported size of IR: {}", PTR_SIZE))),
198124Struct(members) => {
199-let llvm_elements: Vec<BasicTypeEnum> = members
125+let llvm_elements: Vec<_> = members
200126.into_iter()
201-.map(|m| m.ctype.into_llvm_basic(context))
127+.map(|m| m.ctype.into_llvm_basic())
202128.collect::<Result<_, String>>()?;
203-// TODO: allow struct packing
204-Ok(context
205-.struct_type(&llvm_elements, false)
206-.as_basic_type_enum())
129+unimplemented!("struct type -> IR");
207130}
208131// LLVM does not have a union type.
209132// What Clang does is cast it to the type of the largest member,
210133// and then cast every element of the union as it is accessed.
211134// See https://stackoverflow.com/questions/19549942/extracting-a-value-from-an-union#19550613
212135Union(members) => try_max_by_key(members.into_iter().map(|m| m.ctype), Type::sizeof)
213136.expect("parser should ensure all unions have at least one member")?
214-.into_llvm_basic(context),
215-Void | Bitfield(_) | Function(_) => Err(format!("{} is not a basic type", self)),
137+.into_llvm_basic(),
138+Bitfield(_) => unimplemented!("bitfield to llvm type"),
139+Void | Function(_) => Err(format!("{} is not a basic type", self)),
216140}
217141}
218-pub fn into_llvm(self, context: &Context) -> Result<AnyTypeEnum, String> {
142+pub fn into_llvm(self) -> Result<IrType, String> {
219143match self {
220144// basic types (according to LLVM)
221145Bool
@@ -229,18 +153,57 @@ impl Type {
229153 | Pointer(_, _)
230154 | Array(_, _)
231155 | Struct(_)
232- | Union(_) => Ok(self.into_llvm_basic(context)?.as_any_type_enum()),
233-// any type
234-Void => Ok(context.void_type().as_any_type_enum()),
156+ | Bitfield(_)
157+ | Union(_) => self.into_llvm_basic(),
158+// void cannot be loaded or stored
159+Void => Ok(types::INVALID),
160+// I don't think Cranelift IR has a representation for functions
235161Function(_) => unimplemented!("functions to LLVM type"),
236162//Function(func_type) => Ok(ty.to_llvm_basic()?.func_type())
237-// It looks like LLVM has a bitfield type but it isn't exposed by the
238-// Inkwell API? See https://stackoverflow.com/questions/25058213/how-to-spot-a-bit-field-with-clang
239-Bitfield(_) => unimplemented!("bitfield to llvm type"),
240163}
241164}
242165}
243166167+impl FunctionType {
168+pub fn signature(self, location: Location) -> Result<Signature, Locatable<String>> {
169+let params = if self.params.len() == 1 && self.params[0].ctype == Type::Void {
170+// no arguments
171+Vec::new()
172+} else {
173+self.params
174+.into_iter()
175+.map(|param| {
176+ param
177+.ctype
178+.into_llvm_basic()
179+.map(AbiParam::new)
180+.map_err(|err| Locatable {
181+data: err,
182+location: location.clone(),
183+})
184+})
185+.collect::<Result<Vec<_>, Locatable<String>>>()?
186+};
187+let return_type = if *self.return_type == Type::Void {
188+vec![]
189+} else {
190+vec![self
191+.return_type
192+.into_llvm_basic()
193+.map(AbiParam::new)
194+.map_err(|err| Locatable {
195+ data: err,
196+ location,
197+})?]
198+};
199+Ok(Signature {
200+call_conv: *CALLING_CONVENTION,
201+ params,
202+returns: return_type,
203+})
204+}
205+}
206+244207/// partially taken from
245208/// https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2591
246209/// short-circuiting version of iter.max_by_key