GitHub

@@ -1,17 +1,24 @@

11

use std::cmp::max;

22

use 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};

910

use 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+

}

1522

mod x64;

1623

pub 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> {

2937

match 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

3947

Array(t, l) => t.sizeof().and_then(|n| Ok(n * l.length()?)),

4048

Enum(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> {

7078

match self {

7179

Bool

7280

| Char(_)

@@ -86,136 +94,52 @@ impl Type {

8694

Void => 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> {

17998

match 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))),

198124

Struct(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

212135

Union(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> {

219143

match self {

220144

// basic types (according to LLVM)

221145

Bool

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

235161

Function(_) => 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

Read the original on github.com ↗