diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e5310503494..1563178c7af 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,8 +4,8 @@ on: [push, pull_request] env: CARGO_TERM_COLOR: always - DOC_LLVM_FEATURE: llvm14-0 - DOC_LLVM_VERSION: '14.0' + DOC_LLVM_FEATURE: llvm15-0 + DOC_LLVM_VERSION: '15.0' DOC_PATH: target/doc jobs: @@ -27,6 +27,7 @@ jobs: - ["12.0", "12-0"] - ["13.0", "13-0"] - ["14.0", "14-0"] + - ["15.0", "15-0"] steps: - name: Checkout Repo uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index e602b5f151e..8ff45adee2b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk -# VSCode configuration folder -.vscode/ \ No newline at end of file +# IDE project data +/.idea/ +/.vscode/ diff --git a/Cargo.toml b/Cargo.toml index e6a260590ad..cf146be5a2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ llvm11-0 = ["llvm-sys-110"] llvm12-0 = ["llvm-sys-120"] llvm13-0 = ["llvm-sys-130"] llvm14-0 = ["llvm-sys-140"] +llvm15-0 = ["llvm-sys-150"] # Don't link aganist LLVM libraries. This is useful if another dependency is # installing LLVM. See llvm-sys for more details. We can't enable a single # `no-llvm-linking` feature across the board of llvm versions, as it'll cause @@ -41,6 +42,7 @@ llvm11-0-no-llvm-linking = ["llvm11-0", "llvm-sys-110/no-llvm-linking"] llvm12-0-no-llvm-linking = ["llvm12-0", "llvm-sys-120/no-llvm-linking"] llvm13-0-no-llvm-linking = ["llvm13-0", "llvm-sys-130/no-llvm-linking"] llvm14-0-no-llvm-linking = ["llvm14-0", "llvm-sys-140/no-llvm-linking"] +llvm15-0-no-llvm-linking = ["llvm15-0", "llvm-sys-150/no-llvm-linking"] # Don't force linking to libffi on non-windows platforms. Without this feature # inkwell always links to libffi on non-windows platforms. no-libffi-linking = [] @@ -60,6 +62,7 @@ target-bpf = [] target-lanai = [] target-webassembly = [] target-riscv = [] +target-syncvm = [] target-all = [ "target-x86", "target-arm", @@ -76,7 +79,7 @@ target-all = [ "target-bpf", "target-lanai", "target-webassembly", - "target-riscv" + "target-riscv", ] experimental = ["static-alloc"] nightly = ["inkwell_internals/nightly"] @@ -97,7 +100,8 @@ llvm-sys-110 = { package = "llvm-sys", version = "110.0.3", optional = true } llvm-sys-120 = { package = "llvm-sys", version = "120.2.4", optional = true } llvm-sys-130 = { package = "llvm-sys", version = "130.0.4", optional = true } llvm-sys-140 = { package = "llvm-sys", version = "140.0.2", optional = true } -once_cell = "1.4.1" +llvm-sys-150 = { package = "llvm-sys", version = "150.0.3", optional = true } +once_cell = "1.16" parking_lot = "0.12" static-alloc = { version = "0.2", optional = true } diff --git a/examples/kaleidoscope/implementation_typed_pointers.rs b/examples/kaleidoscope/implementation_typed_pointers.rs new file mode 100644 index 00000000000..3713f57def6 --- /dev/null +++ b/examples/kaleidoscope/implementation_typed_pointers.rs @@ -0,0 +1,1200 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::iter::Peekable; +use std::ops::DerefMut; +use std::str::Chars; + +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::module::Module; +use inkwell::passes::PassManager; +use inkwell::types::BasicMetadataTypeEnum; +use inkwell::values::{BasicMetadataValueEnum, FloatValue, FunctionValue, PointerValue}; +use inkwell::FloatPredicate; + +use crate::Token::*; + +const ANONYMOUS_FUNCTION_NAME: &str = "anonymous"; + +// ====================================================================================== +// LEXER ================================================================================ +// ====================================================================================== + +/// Represents a primitive syntax token. +#[derive(Debug, Clone)] +pub enum Token { + Binary, + Comma, + Comment, + Def, + Else, + EOF, + Extern, + For, + Ident(String), + If, + In, + LParen, + Number(f64), + Op(char), + RParen, + Then, + Unary, + Var, +} + +/// Defines an error encountered by the `Lexer`. +pub struct LexError { + pub error: &'static str, + pub index: usize, +} + +impl LexError { + #[allow(unused)] + pub fn new(msg: &'static str) -> LexError { + LexError { error: msg, index: 0 } + } + + #[allow(unused)] + pub fn with_index(msg: &'static str, index: usize) -> LexError { + LexError { error: msg, index } + } +} + +/// Defines the result of a lexing operation; namely a +/// `Token` on success, or a `LexError` on failure. +pub type LexResult = Result; + +/// Defines a lexer which transforms an input `String` into +/// a `Token` stream. +pub struct Lexer<'a> { + input: &'a str, + chars: Box>>, + pos: usize, +} + +impl<'a> Lexer<'a> { + /// Creates a new `Lexer`, given its source `input`. + pub fn new(input: &'a str) -> Lexer<'a> { + Lexer { + input, + chars: Box::new(input.chars().peekable()), + pos: 0, + } + } + + /// Lexes and returns the next `Token` from the source code. + pub fn lex(&mut self) -> LexResult { + let chars = self.chars.deref_mut(); + let src = self.input; + + let mut pos = self.pos; + + // Skip whitespaces + loop { + // Note: the following lines are in their own scope to + // limit how long 'chars' is borrowed, and in order to allow + // it to be borrowed again in the loop by 'chars.next()'. + { + let ch = chars.peek(); + + if ch.is_none() { + self.pos = pos; + + return Ok(Token::EOF); + } + + if !ch.unwrap().is_whitespace() { + break; + } + } + + chars.next(); + pos += 1; + } + + let start = pos; + let next = chars.next(); + + if next.is_none() { + return Ok(Token::EOF); + } + + pos += 1; + + // Actually get the next token. + let result = match next.unwrap() { + '(' => Ok(Token::LParen), + ')' => Ok(Token::RParen), + ',' => Ok(Token::Comma), + + '#' => { + // Comment + loop { + let ch = chars.next(); + pos += 1; + + if ch == Some('\n') { + break; + } + } + + Ok(Token::Comment) + }, + + '.' | '0'..='9' => { + // Parse number literal + loop { + let ch = match chars.peek() { + Some(ch) => *ch, + None => return Ok(Token::EOF), + }; + + // Parse float. + if ch != '.' && !ch.is_ascii_hexdigit() { + break; + } + + chars.next(); + pos += 1; + } + + Ok(Token::Number(src[start..pos].parse().unwrap())) + }, + + 'a'..='z' | 'A'..='Z' | '_' => { + // Parse identifier + loop { + let ch = match chars.peek() { + Some(ch) => *ch, + None => return Ok(Token::EOF), + }; + + // A word-like identifier only contains underscores and alphanumeric characters. + if ch != '_' && !ch.is_alphanumeric() { + break; + } + + chars.next(); + pos += 1; + } + + match &src[start..pos] { + "def" => Ok(Token::Def), + "extern" => Ok(Token::Extern), + "if" => Ok(Token::If), + "then" => Ok(Token::Then), + "else" => Ok(Token::Else), + "for" => Ok(Token::For), + "in" => Ok(Token::In), + "unary" => Ok(Token::Unary), + "binary" => Ok(Token::Binary), + "var" => Ok(Token::Var), + + ident => Ok(Token::Ident(ident.to_string())), + } + }, + + op => { + // Parse operator + Ok(Token::Op(op)) + }, + }; + + // Update stored position, and return + self.pos = pos; + + result + } +} + +impl<'a> Iterator for Lexer<'a> { + type Item = Token; + + /// Lexes the next `Token` and returns it. + /// On EOF or failure, `None` will be returned. + fn next(&mut self) -> Option { + match self.lex() { + Ok(EOF) | Err(_) => None, + Ok(token) => Some(token), + } + } +} + +// ====================================================================================== +// PARSER =============================================================================== +// ====================================================================================== + +/// Defines a primitive expression. +#[derive(Debug)] +pub enum Expr { + Binary { + op: char, + left: Box, + right: Box, + }, + + Call { + fn_name: String, + args: Vec, + }, + + Conditional { + cond: Box, + consequence: Box, + alternative: Box, + }, + + For { + var_name: String, + start: Box, + end: Box, + step: Option>, + body: Box, + }, + + Number(f64), + + Variable(String), + + VarIn { + variables: Vec<(String, Option)>, + body: Box, + }, +} + +/// Defines the prototype (name and parameters) of a function. +#[derive(Debug)] +pub struct Prototype { + pub name: String, + pub args: Vec, + pub is_op: bool, + pub prec: usize, +} + +/// Defines a user-defined or external function. +#[derive(Debug)] +pub struct Function { + pub prototype: Prototype, + pub body: Option, + pub is_anon: bool, +} + +/// Represents the `Expr` parser. +pub struct Parser<'a> { + tokens: Vec, + pos: usize, + prec: &'a mut HashMap, +} + +// I'm ignoring the 'must_use' lint in order to call 'self.advance' without checking +// the result when an EOF is acceptable. +#[allow(unused_must_use)] +impl<'a> Parser<'a> { + /// Creates a new parser, given an input `str` and a `HashMap` binding + /// an operator and its precedence in binary expressions. + pub fn new(input: String, op_precedence: &'a mut HashMap) -> Self { + let mut lexer = Lexer::new(input.as_str()); + let tokens = lexer.by_ref().collect(); + + Parser { + tokens, + prec: op_precedence, + pos: 0, + } + } + + /// Parses the content of the parser. + pub fn parse(&mut self) -> Result { + let result = match self.current()? { + Def => self.parse_def(), + Extern => self.parse_extern(), + _ => self.parse_toplevel_expr(), + }; + + match result { + Ok(result) => { + if !self.at_end() { + Err("Unexpected token after parsed expression.") + } else { + Ok(result) + } + }, + + err => err, + } + } + + /// Returns the current `Token`, without performing safety checks beforehand. + fn curr(&self) -> Token { + self.tokens[self.pos].clone() + } + + /// Returns the current `Token`, or an error that + /// indicates that the end of the file has been unexpectedly reached if it is the case. + fn current(&self) -> Result { + if self.pos >= self.tokens.len() { + Err("Unexpected end of file.") + } else { + Ok(self.tokens[self.pos].clone()) + } + } + + /// Advances the position, and returns an empty `Result` whose error + /// indicates that the end of the file has been unexpectedly reached. + /// This allows to use the `self.advance()?;` syntax. + fn advance(&mut self) -> Result<(), &'static str> { + let npos = self.pos + 1; + + self.pos = npos; + + if npos < self.tokens.len() { + Ok(()) + } else { + Err("Unexpected end of file.") + } + } + + /// Returns a value indicating whether or not the `Parser` + /// has reached the end of the input. + fn at_end(&self) -> bool { + self.pos >= self.tokens.len() + } + + /// Returns the precedence of the current `Token`, or 0 if it is not recognized as a binary operator. + fn get_tok_precedence(&self) -> i32 { + if let Ok(Op(op)) = self.current() { + *self.prec.get(&op).unwrap_or(&100) + } else { + -1 + } + } + + /// Parses the prototype of a function, whether external or user-defined. + fn parse_prototype(&mut self) -> Result { + let (id, is_operator, precedence) = match self.curr() { + Ident(id) => { + self.advance()?; + + (id, false, 0) + }, + + Binary => { + self.advance()?; + + let op = match self.curr() { + Op(ch) => ch, + _ => return Err("Expected operator in custom operator declaration."), + }; + + self.advance()?; + + let mut name = String::from("binary"); + + name.push(op); + + let prec = if let Number(prec) = self.curr() { + self.advance()?; + + prec as usize + } else { + 0 + }; + + self.prec.insert(op, prec as i32); + + (name, true, prec) + }, + + Unary => { + self.advance()?; + + let op = match self.curr() { + Op(ch) => ch, + _ => return Err("Expected operator in custom operator declaration."), + }; + + let mut name = String::from("unary"); + + name.push(op); + + self.advance()?; + + (name, true, 0) + }, + + _ => return Err("Expected identifier in prototype declaration."), + }; + + match self.curr() { + LParen => (), + _ => return Err("Expected '(' character in prototype declaration."), + } + + self.advance()?; + + if let RParen = self.curr() { + self.advance(); + + return Ok(Prototype { + name: id, + args: vec![], + is_op: is_operator, + prec: precedence, + }); + } + + let mut args = vec![]; + + loop { + match self.curr() { + Ident(name) => args.push(name), + _ => return Err("Expected identifier in parameter declaration."), + } + + self.advance()?; + + match self.curr() { + RParen => { + self.advance(); + break; + }, + Comma => { + self.advance(); + }, + _ => return Err("Expected ',' or ')' character in prototype declaration."), + } + } + + Ok(Prototype { + name: id, + args, + is_op: is_operator, + prec: precedence, + }) + } + + /// Parses a user-defined function. + fn parse_def(&mut self) -> Result { + // Eat 'def' keyword + self.pos += 1; + + // Parse signature of function + let proto = self.parse_prototype()?; + + // Parse body of function + let body = self.parse_expr()?; + + // Return new function + Ok(Function { + prototype: proto, + body: Some(body), + is_anon: false, + }) + } + + /// Parses an external function declaration. + fn parse_extern(&mut self) -> Result { + // Eat 'extern' keyword + self.pos += 1; + + // Parse signature of extern function + let proto = self.parse_prototype()?; + + Ok(Function { + prototype: proto, + body: None, + is_anon: false, + }) + } + + /// Parses any expression. + fn parse_expr(&mut self) -> Result { + match self.parse_unary_expr() { + Ok(left) => self.parse_binary_expr(0, left), + err => err, + } + } + + /// Parses a literal number. + fn parse_nb_expr(&mut self) -> Result { + // Simply convert Token::Number to Expr::Number + match self.curr() { + Number(nb) => { + self.advance(); + Ok(Expr::Number(nb)) + }, + _ => Err("Expected number literal."), + } + } + + /// Parses an expression enclosed in parenthesis. + fn parse_paren_expr(&mut self) -> Result { + match self.current()? { + LParen => (), + _ => return Err("Expected '(' character at start of parenthesized expression."), + } + + self.advance()?; + + let expr = self.parse_expr()?; + + match self.current()? { + RParen => (), + _ => return Err("Expected ')' character at end of parenthesized expression."), + } + + self.advance(); + + Ok(expr) + } + + /// Parses an expression that starts with an identifier (either a variable or a function call). + fn parse_id_expr(&mut self) -> Result { + let id = match self.curr() { + Ident(id) => id, + _ => return Err("Expected identifier."), + }; + + if self.advance().is_err() { + return Ok(Expr::Variable(id)); + } + + match self.curr() { + LParen => { + self.advance()?; + + if let RParen = self.curr() { + return Ok(Expr::Call { + fn_name: id, + args: vec![], + }); + } + + let mut args = vec![]; + + loop { + args.push(self.parse_expr()?); + + match self.current()? { + Comma => (), + RParen => break, + _ => return Err("Expected ',' character in function call."), + } + + self.advance()?; + } + + self.advance(); + + Ok(Expr::Call { fn_name: id, args }) + }, + + _ => Ok(Expr::Variable(id)), + } + } + + /// Parses an unary expression. + fn parse_unary_expr(&mut self) -> Result { + let op = match self.current()? { + Op(ch) => { + self.advance()?; + ch + }, + _ => return self.parse_primary(), + }; + + let mut name = String::from("unary"); + + name.push(op); + + Ok(Expr::Call { + fn_name: name, + args: vec![self.parse_unary_expr()?], + }) + } + + /// Parses a binary expression, given its left-hand expression. + fn parse_binary_expr(&mut self, prec: i32, mut left: Expr) -> Result { + loop { + let curr_prec = self.get_tok_precedence(); + + if curr_prec < prec || self.at_end() { + return Ok(left); + } + + let op = match self.curr() { + Op(op) => op, + _ => return Err("Invalid operator."), + }; + + self.advance()?; + + let mut right = self.parse_unary_expr()?; + + let next_prec = self.get_tok_precedence(); + + if curr_prec < next_prec { + right = self.parse_binary_expr(curr_prec + 1, right)?; + } + + left = Expr::Binary { + op, + left: Box::new(left), + right: Box::new(right), + }; + } + } + + /// Parses a conditional if..then..else expression. + fn parse_conditional_expr(&mut self) -> Result { + // eat 'if' token + self.advance()?; + + let cond = self.parse_expr()?; + + // eat 'then' token + match self.current() { + Ok(Then) => self.advance()?, + _ => return Err("Expected 'then' keyword."), + } + + let then = self.parse_expr()?; + + // eat 'else' token + match self.current() { + Ok(Else) => self.advance()?, + _ => return Err("Expected 'else' keyword."), + } + + let otherwise = self.parse_expr()?; + + Ok(Expr::Conditional { + cond: Box::new(cond), + consequence: Box::new(then), + alternative: Box::new(otherwise), + }) + } + + /// Parses a loop for..in.. expression. + fn parse_for_expr(&mut self) -> Result { + // eat 'for' token + self.advance()?; + + let name = match self.curr() { + Ident(n) => n, + _ => return Err("Expected identifier in for loop."), + }; + + // eat identifier + self.advance()?; + + // eat '=' token + match self.curr() { + Op('=') => self.advance()?, + _ => return Err("Expected '=' character in for loop."), + } + + let start = self.parse_expr()?; + + // eat ',' token + match self.current()? { + Comma => self.advance()?, + _ => return Err("Expected ',' character in for loop."), + } + + let end = self.parse_expr()?; + + // parse (optional) step expression + let step = match self.current()? { + Comma => { + self.advance()?; + + Some(self.parse_expr()?) + }, + + _ => None, + }; + + // eat 'in' token + match self.current()? { + In => self.advance()?, + _ => return Err("Expected 'in' keyword in for loop."), + } + + let body = self.parse_expr()?; + + Ok(Expr::For { + var_name: name, + start: Box::new(start), + end: Box::new(end), + step: step.map(Box::new), + body: Box::new(body), + }) + } + + /// Parses a var..in expression. + fn parse_var_expr(&mut self) -> Result { + // eat 'var' token + self.advance()?; + + let mut variables = Vec::new(); + + // parse variables + loop { + let name = match self.curr() { + Ident(name) => name, + _ => return Err("Expected identifier in 'var..in' declaration."), + }; + + self.advance()?; + + // read (optional) initializer + let initializer = match self.curr() { + Op('=') => Some({ + self.advance()?; + self.parse_expr()? + }), + + _ => None, + }; + + variables.push((name, initializer)); + + match self.curr() { + Comma => { + self.advance()?; + }, + In => { + self.advance()?; + break; + }, + _ => return Err("Expected comma or 'in' keyword in variable declaration."), + } + } + + // parse body + let body = self.parse_expr()?; + + Ok(Expr::VarIn { + variables, + body: Box::new(body), + }) + } + + /// Parses a primary expression (an identifier, a number or a parenthesized expression). + fn parse_primary(&mut self) -> Result { + match self.curr() { + Ident(_) => self.parse_id_expr(), + Number(_) => self.parse_nb_expr(), + LParen => self.parse_paren_expr(), + If => self.parse_conditional_expr(), + For => self.parse_for_expr(), + Var => self.parse_var_expr(), + _ => Err("Unknown expression."), + } + } + + /// Parses a top-level expression and makes an anonymous function out of it, + /// for easier compilation. + fn parse_toplevel_expr(&mut self) -> Result { + match self.parse_expr() { + Ok(expr) => Ok(Function { + prototype: Prototype { + name: ANONYMOUS_FUNCTION_NAME.to_string(), + args: vec![], + is_op: false, + prec: 0, + }, + body: Some(expr), + is_anon: true, + }), + + Err(err) => Err(err), + } + } +} + +// ====================================================================================== +// COMPILER ============================================================================= +// ====================================================================================== + +/// Defines the `Expr` compiler. +pub struct Compiler<'a, 'ctx> { + pub context: &'ctx Context, + pub builder: &'a Builder<'ctx>, + pub fpm: &'a PassManager>, + pub module: &'a Module<'ctx>, + pub function: &'a Function, + + variables: HashMap>, + fn_value_opt: Option>, +} + +impl<'a, 'ctx> Compiler<'a, 'ctx> { + /// Gets a defined function given its name. + #[inline] + fn get_function(&self, name: &str) -> Option> { + self.module.get_function(name) + } + + /// Returns the `FunctionValue` representing the function being compiled. + #[inline] + fn fn_value(&self) -> FunctionValue<'ctx> { + self.fn_value_opt.unwrap() + } + + /// Creates a new stack allocation instruction in the entry block of the function. + fn create_entry_block_alloca(&self, name: &str) -> PointerValue<'ctx> { + let builder = self.context.create_builder(); + + let entry = self.fn_value().get_first_basic_block().unwrap(); + + match entry.get_first_instruction() { + Some(first_instr) => builder.position_before(&first_instr), + None => builder.position_at_end(entry), + } + + builder.build_alloca(self.context.f64_type(), name) + } + + /// Compiles the specified `Expr` into an LLVM `FloatValue`. + fn compile_expr(&mut self, expr: &Expr) -> Result, &'static str> { + match *expr { + Expr::Number(nb) => Ok(self.context.f64_type().const_float(nb)), + + Expr::Variable(ref name) => match self.variables.get(name.as_str()) { + Some(var) => Ok(self.builder.build_load(*var, name.as_str()).into_float_value()), + None => Err("Could not find a matching variable."), + }, + + Expr::VarIn { + ref variables, + ref body, + } => { + let mut old_bindings = Vec::new(); + + for &(ref var_name, ref initializer) in variables { + let var_name = var_name.as_str(); + + let initial_val = match *initializer { + Some(ref init) => self.compile_expr(init)?, + None => self.context.f64_type().const_float(0.), + }; + + let alloca = self.create_entry_block_alloca(var_name); + + self.builder.build_store(alloca, initial_val); + + if let Some(old_binding) = self.variables.remove(var_name) { + old_bindings.push(old_binding); + } + + self.variables.insert(var_name.to_string(), alloca); + } + + let body = self.compile_expr(body)?; + + for binding in old_bindings { + self.variables + .insert(binding.get_name().to_str().unwrap().to_string(), binding); + } + + Ok(body) + }, + + Expr::Binary { + op, + ref left, + ref right, + } => { + if op == '=' { + // handle assignement + let var_name = match *left.borrow() { + Expr::Variable(ref var_name) => var_name, + _ => { + return Err("Expected variable as left-hand operator of assignement."); + }, + }; + + let var_val = self.compile_expr(right)?; + let var = self.variables.get(var_name.as_str()).ok_or("Undefined variable.")?; + + self.builder.build_store(*var, var_val); + + Ok(var_val) + } else { + let lhs = self.compile_expr(left)?; + let rhs = self.compile_expr(right)?; + + match op { + '+' => Ok(self.builder.build_float_add(lhs, rhs, "tmpadd")), + '-' => Ok(self.builder.build_float_sub(lhs, rhs, "tmpsub")), + '*' => Ok(self.builder.build_float_mul(lhs, rhs, "tmpmul")), + '/' => Ok(self.builder.build_float_div(lhs, rhs, "tmpdiv")), + '<' => Ok({ + let cmp = self + .builder + .build_float_compare(FloatPredicate::ULT, lhs, rhs, "tmpcmp"); + + self.builder + .build_unsigned_int_to_float(cmp, self.context.f64_type(), "tmpbool") + }), + '>' => Ok({ + let cmp = self + .builder + .build_float_compare(FloatPredicate::ULT, rhs, lhs, "tmpcmp"); + + self.builder + .build_unsigned_int_to_float(cmp, self.context.f64_type(), "tmpbool") + }), + + custom => { + let mut name = String::from("binary"); + + name.push(custom); + + match self.get_function(name.as_str()) { + Some(fun) => { + match self + .builder + .build_call(fun, &[lhs.into(), rhs.into()], "tmpbin") + .try_as_basic_value() + .left() + { + Some(value) => Ok(value.into_float_value()), + None => Err("Invalid call produced."), + } + }, + + None => Err("Undefined binary operator."), + } + }, + } + } + }, + + Expr::Call { ref fn_name, ref args } => match self.get_function(fn_name.as_str()) { + Some(fun) => { + let mut compiled_args = Vec::with_capacity(args.len()); + + for arg in args { + compiled_args.push(self.compile_expr(arg)?); + } + + let argsv: Vec = + compiled_args.iter().by_ref().map(|&val| val.into()).collect(); + + match self + .builder + .build_call(fun, argsv.as_slice(), "tmp") + .try_as_basic_value() + .left() + { + Some(value) => Ok(value.into_float_value()), + None => Err("Invalid call produced."), + } + }, + None => Err("Unknown function."), + }, + + Expr::Conditional { + ref cond, + ref consequence, + ref alternative, + } => { + let parent = self.fn_value(); + let zero_const = self.context.f64_type().const_float(0.0); + + // create condition by comparing without 0.0 and returning an int + let cond = self.compile_expr(cond)?; + let cond = self + .builder + .build_float_compare(FloatPredicate::ONE, cond, zero_const, "ifcond"); + + // build branch + let then_bb = self.context.append_basic_block(parent, "then"); + let else_bb = self.context.append_basic_block(parent, "else"); + let cont_bb = self.context.append_basic_block(parent, "ifcont"); + + self.builder.build_conditional_branch(cond, then_bb, else_bb); + + // build then block + self.builder.position_at_end(then_bb); + let then_val = self.compile_expr(consequence)?; + self.builder.build_unconditional_branch(cont_bb); + + let then_bb = self.builder.get_insert_block().unwrap(); + + // build else block + self.builder.position_at_end(else_bb); + let else_val = self.compile_expr(alternative)?; + self.builder.build_unconditional_branch(cont_bb); + + let else_bb = self.builder.get_insert_block().unwrap(); + + // emit merge block + self.builder.position_at_end(cont_bb); + + let phi = self.builder.build_phi(self.context.f64_type(), "iftmp"); + + phi.add_incoming(&[(&then_val, then_bb), (&else_val, else_bb)]); + + Ok(phi.as_basic_value().into_float_value()) + }, + + Expr::For { + ref var_name, + ref start, + ref end, + ref step, + ref body, + } => { + let parent = self.fn_value(); + + let start_alloca = self.create_entry_block_alloca(var_name); + let start = self.compile_expr(start)?; + + self.builder.build_store(start_alloca, start); + + // go from current block to loop block + let loop_bb = self.context.append_basic_block(parent, "loop"); + + self.builder.build_unconditional_branch(loop_bb); + self.builder.position_at_end(loop_bb); + + let old_val = self.variables.remove(var_name.as_str()); + + self.variables.insert(var_name.to_owned(), start_alloca); + + // emit body + self.compile_expr(body)?; + + // emit step + let step = match *step { + Some(ref step) => self.compile_expr(step)?, + None => self.context.f64_type().const_float(1.0), + }; + + // compile end condition + let end_cond = self.compile_expr(end)?; + + let curr_var = self.builder.build_load(start_alloca, var_name); + let next_var = self + .builder + .build_float_add(curr_var.into_float_value(), step, "nextvar"); + + self.builder.build_store(start_alloca, next_var); + + let end_cond = self.builder.build_float_compare( + FloatPredicate::ONE, + end_cond, + self.context.f64_type().const_float(0.0), + "loopcond", + ); + let after_bb = self.context.append_basic_block(parent, "afterloop"); + + self.builder.build_conditional_branch(end_cond, loop_bb, after_bb); + self.builder.position_at_end(after_bb); + + self.variables.remove(var_name); + + if let Some(val) = old_val { + self.variables.insert(var_name.to_owned(), val); + } + + Ok(self.context.f64_type().const_float(0.0)) + }, + } + } + + /// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`. + fn compile_prototype(&self, proto: &Prototype) -> Result, &'static str> { + let ret_type = self.context.f64_type(); + let args_types = std::iter::repeat(ret_type) + .take(proto.args.len()) + .map(|f| f.into()) + .collect::>(); + let args_types = args_types.as_slice(); + + let fn_type = self.context.f64_type().fn_type(args_types, false); + let fn_val = self.module.add_function(proto.name.as_str(), fn_type, None); + + // set arguments names + for (i, arg) in fn_val.get_param_iter().enumerate() { + arg.into_float_value().set_name(proto.args[i].as_str()); + } + + // finally return built prototype + Ok(fn_val) + } + + /// Compiles the specified `Function` into an LLVM `FunctionValue`. + fn compile_fn(&mut self) -> Result, &'static str> { + let proto = &self.function.prototype; + let function = self.compile_prototype(proto)?; + + // got external function, returning only compiled prototype + if self.function.body.is_none() { + return Ok(function); + } + + let entry = self.context.append_basic_block(function, "entry"); + + self.builder.position_at_end(entry); + + // update fn field + self.fn_value_opt = Some(function); + + // build variables map + self.variables.reserve(proto.args.len()); + + for (i, arg) in function.get_param_iter().enumerate() { + let arg_name = proto.args[i].as_str(); + let alloca = self.create_entry_block_alloca(arg_name); + + self.builder.build_store(alloca, arg); + + self.variables.insert(proto.args[i].clone(), alloca); + } + + // compile body + let body = self.compile_expr(self.function.body.as_ref().unwrap())?; + + self.builder.build_return(Some(&body)); + + // return the whole thing after verification and optimization + if function.verify(true) { + self.fpm.run_on(&function); + + Ok(function) + } else { + unsafe { + function.delete(); + } + + Err("Invalid generated function.") + } + } + + /// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`. + pub fn compile( + context: &'ctx Context, + builder: &'a Builder<'ctx>, + pass_manager: &'a PassManager>, + module: &'a Module<'ctx>, + function: &Function, + ) -> Result, &'static str> { + let mut compiler = Compiler { + context, + builder, + fpm: pass_manager, + module, + function, + fn_value_opt: None, + variables: HashMap::new(), + }; + + compiler.compile_fn() + } +} diff --git a/examples/kaleidoscope/main.rs b/examples/kaleidoscope/main.rs index e64d976ce00..3243dc3a258 100644 --- a/examples/kaleidoscope/main.rs +++ b/examples/kaleidoscope/main.rs @@ -12,1205 +12,20 @@ //! Both the `Parser` and the `Compiler` may fail, in which case they would return //! an error represented by `Result`, for easier error reporting. -use std::borrow::Borrow; use std::collections::HashMap; use std::io::{self, Write}; -use std::iter::Peekable; -use std::ops::DerefMut; -use std::str::Chars; -use inkwell::builder::Builder; use inkwell::context::Context; -use inkwell::module::Module; use inkwell::passes::PassManager; -use inkwell::types::BasicMetadataTypeEnum; -use inkwell::values::{BasicMetadataValueEnum, FloatValue, FunctionValue, PointerValue}; -use inkwell::{FloatPredicate, OptimizationLevel}; +use inkwell::OptimizationLevel; -use crate::Token::*; +use inkwell_internals::llvm_versions; -const ANONYMOUS_FUNCTION_NAME: &str = "anonymous"; +#[cfg(not(any(feature = "llvm15-0")))] +mod implementation_typed_pointers; -// ====================================================================================== -// LEXER ================================================================================ -// ====================================================================================== - -/// Represents a primitive syntax token. -#[derive(Debug, Clone)] -pub enum Token { - Binary, - Comma, - Comment, - Def, - Else, - EOF, - Extern, - For, - Ident(String), - If, - In, - LParen, - Number(f64), - Op(char), - RParen, - Then, - Unary, - Var, -} - -/// Defines an error encountered by the `Lexer`. -pub struct LexError { - pub error: &'static str, - pub index: usize, -} - -impl LexError { - pub fn new(msg: &'static str) -> LexError { - LexError { error: msg, index: 0 } - } - - pub fn with_index(msg: &'static str, index: usize) -> LexError { - LexError { error: msg, index } - } -} - -/// Defines the result of a lexing operation; namely a -/// `Token` on success, or a `LexError` on failure. -pub type LexResult = Result; - -/// Defines a lexer which transforms an input `String` into -/// a `Token` stream. -pub struct Lexer<'a> { - input: &'a str, - chars: Box>>, - pos: usize, -} - -impl<'a> Lexer<'a> { - /// Creates a new `Lexer`, given its source `input`. - pub fn new(input: &'a str) -> Lexer<'a> { - Lexer { - input, - chars: Box::new(input.chars().peekable()), - pos: 0, - } - } - - /// Lexes and returns the next `Token` from the source code. - pub fn lex(&mut self) -> LexResult { - let chars = self.chars.deref_mut(); - let src = self.input; - - let mut pos = self.pos; - - // Skip whitespaces - loop { - // Note: the following lines are in their own scope to - // limit how long 'chars' is borrowed, and in order to allow - // it to be borrowed again in the loop by 'chars.next()'. - { - let ch = chars.peek(); - - if ch.is_none() { - self.pos = pos; - - return Ok(Token::EOF); - } - - if !ch.unwrap().is_whitespace() { - break; - } - } - - chars.next(); - pos += 1; - } - - let start = pos; - let next = chars.next(); - - if next.is_none() { - return Ok(Token::EOF); - } - - pos += 1; - - // Actually get the next token. - let result = match next.unwrap() { - '(' => Ok(Token::LParen), - ')' => Ok(Token::RParen), - ',' => Ok(Token::Comma), - - '#' => { - // Comment - loop { - let ch = chars.next(); - pos += 1; - - if ch == Some('\n') { - break; - } - } - - Ok(Token::Comment) - }, - - '.' | '0'..='9' => { - // Parse number literal - loop { - let ch = match chars.peek() { - Some(ch) => *ch, - None => return Ok(Token::EOF), - }; - - // Parse float. - if ch != '.' && !ch.is_ascii_hexdigit() { - break; - } - - chars.next(); - pos += 1; - } - - Ok(Token::Number(src[start..pos].parse().unwrap())) - }, - - 'a'..='z' | 'A'..='Z' | '_' => { - // Parse identifier - loop { - let ch = match chars.peek() { - Some(ch) => *ch, - None => return Ok(Token::EOF), - }; - - // A word-like identifier only contains underscores and alphanumeric characters. - if ch != '_' && !ch.is_alphanumeric() { - break; - } - - chars.next(); - pos += 1; - } - - match &src[start..pos] { - "def" => Ok(Token::Def), - "extern" => Ok(Token::Extern), - "if" => Ok(Token::If), - "then" => Ok(Token::Then), - "else" => Ok(Token::Else), - "for" => Ok(Token::For), - "in" => Ok(Token::In), - "unary" => Ok(Token::Unary), - "binary" => Ok(Token::Binary), - "var" => Ok(Token::Var), - - ident => Ok(Token::Ident(ident.to_string())), - } - }, - - op => { - // Parse operator - Ok(Token::Op(op)) - }, - }; - - // Update stored position, and return - self.pos = pos; - - result - } -} - -impl<'a> Iterator for Lexer<'a> { - type Item = Token; - - /// Lexes the next `Token` and returns it. - /// On EOF or failure, `None` will be returned. - fn next(&mut self) -> Option { - match self.lex() { - Ok(EOF) | Err(_) => None, - Ok(token) => Some(token), - } - } -} - -// ====================================================================================== -// PARSER =============================================================================== -// ====================================================================================== - -/// Defines a primitive expression. -#[derive(Debug)] -pub enum Expr { - Binary { - op: char, - left: Box, - right: Box, - }, - - Call { - fn_name: String, - args: Vec, - }, - - Conditional { - cond: Box, - consequence: Box, - alternative: Box, - }, - - For { - var_name: String, - start: Box, - end: Box, - step: Option>, - body: Box, - }, - - Number(f64), - - Variable(String), - - VarIn { - variables: Vec<(String, Option)>, - body: Box, - }, -} - -/// Defines the prototype (name and parameters) of a function. -#[derive(Debug)] -pub struct Prototype { - pub name: String, - pub args: Vec, - pub is_op: bool, - pub prec: usize, -} - -/// Defines a user-defined or external function. -#[derive(Debug)] -pub struct Function { - pub prototype: Prototype, - pub body: Option, - pub is_anon: bool, -} - -/// Represents the `Expr` parser. -pub struct Parser<'a> { - tokens: Vec, - pos: usize, - prec: &'a mut HashMap, -} - -// I'm ignoring the 'must_use' lint in order to call 'self.advance' without checking -// the result when an EOF is acceptable. -#[allow(unused_must_use)] -impl<'a> Parser<'a> { - /// Creates a new parser, given an input `str` and a `HashMap` binding - /// an operator and its precedence in binary expressions. - pub fn new(input: String, op_precedence: &'a mut HashMap) -> Self { - let mut lexer = Lexer::new(input.as_str()); - let tokens = lexer.by_ref().collect(); - - Parser { - tokens, - prec: op_precedence, - pos: 0, - } - } - - /// Parses the content of the parser. - pub fn parse(&mut self) -> Result { - let result = match self.current()? { - Def => self.parse_def(), - Extern => self.parse_extern(), - _ => self.parse_toplevel_expr(), - }; - - match result { - Ok(result) => { - if !self.at_end() { - Err("Unexpected token after parsed expression.") - } else { - Ok(result) - } - }, - - err => err, - } - } - - /// Returns the current `Token`, without performing safety checks beforehand. - fn curr(&self) -> Token { - self.tokens[self.pos].clone() - } - - /// Returns the current `Token`, or an error that - /// indicates that the end of the file has been unexpectedly reached if it is the case. - fn current(&self) -> Result { - if self.pos >= self.tokens.len() { - Err("Unexpected end of file.") - } else { - Ok(self.tokens[self.pos].clone()) - } - } - - /// Advances the position, and returns an empty `Result` whose error - /// indicates that the end of the file has been unexpectedly reached. - /// This allows to use the `self.advance()?;` syntax. - fn advance(&mut self) -> Result<(), &'static str> { - let npos = self.pos + 1; - - self.pos = npos; - - if npos < self.tokens.len() { - Ok(()) - } else { - Err("Unexpected end of file.") - } - } - - /// Returns a value indicating whether or not the `Parser` - /// has reached the end of the input. - fn at_end(&self) -> bool { - self.pos >= self.tokens.len() - } - - /// Returns the precedence of the current `Token`, or 0 if it is not recognized as a binary operator. - fn get_tok_precedence(&self) -> i32 { - if let Ok(Op(op)) = self.current() { - *self.prec.get(&op).unwrap_or(&100) - } else { - -1 - } - } - - /// Parses the prototype of a function, whether external or user-defined. - fn parse_prototype(&mut self) -> Result { - let (id, is_operator, precedence) = match self.curr() { - Ident(id) => { - self.advance()?; - - (id, false, 0) - }, - - Binary => { - self.advance()?; - - let op = match self.curr() { - Op(ch) => ch, - _ => return Err("Expected operator in custom operator declaration."), - }; - - self.advance()?; - - let mut name = String::from("binary"); - - name.push(op); - - let prec = if let Number(prec) = self.curr() { - self.advance()?; - - prec as usize - } else { - 0 - }; - - self.prec.insert(op, prec as i32); - - (name, true, prec) - }, - - Unary => { - self.advance()?; - - let op = match self.curr() { - Op(ch) => ch, - _ => return Err("Expected operator in custom operator declaration."), - }; - - let mut name = String::from("unary"); - - name.push(op); - - self.advance()?; - - (name, true, 0) - }, - - _ => return Err("Expected identifier in prototype declaration."), - }; - - match self.curr() { - LParen => (), - _ => return Err("Expected '(' character in prototype declaration."), - } - - self.advance()?; - - if let RParen = self.curr() { - self.advance(); - - return Ok(Prototype { - name: id, - args: vec![], - is_op: is_operator, - prec: precedence, - }); - } - - let mut args = vec![]; - - loop { - match self.curr() { - Ident(name) => args.push(name), - _ => return Err("Expected identifier in parameter declaration."), - } - - self.advance()?; - - match self.curr() { - RParen => { - self.advance(); - break; - }, - Comma => { - self.advance(); - }, - _ => return Err("Expected ',' or ')' character in prototype declaration."), - } - } - - Ok(Prototype { - name: id, - args, - is_op: is_operator, - prec: precedence, - }) - } - - /// Parses a user-defined function. - fn parse_def(&mut self) -> Result { - // Eat 'def' keyword - self.pos += 1; - - // Parse signature of function - let proto = self.parse_prototype()?; - - // Parse body of function - let body = self.parse_expr()?; - - // Return new function - Ok(Function { - prototype: proto, - body: Some(body), - is_anon: false, - }) - } - - /// Parses an external function declaration. - fn parse_extern(&mut self) -> Result { - // Eat 'extern' keyword - self.pos += 1; - - // Parse signature of extern function - let proto = self.parse_prototype()?; - - Ok(Function { - prototype: proto, - body: None, - is_anon: false, - }) - } - - /// Parses any expression. - fn parse_expr(&mut self) -> Result { - match self.parse_unary_expr() { - Ok(left) => self.parse_binary_expr(0, left), - err => err, - } - } - - /// Parses a literal number. - fn parse_nb_expr(&mut self) -> Result { - // Simply convert Token::Number to Expr::Number - match self.curr() { - Number(nb) => { - self.advance(); - Ok(Expr::Number(nb)) - }, - _ => Err("Expected number literal."), - } - } - - /// Parses an expression enclosed in parenthesis. - fn parse_paren_expr(&mut self) -> Result { - match self.current()? { - LParen => (), - _ => return Err("Expected '(' character at start of parenthesized expression."), - } - - self.advance()?; - - let expr = self.parse_expr()?; - - match self.current()? { - RParen => (), - _ => return Err("Expected ')' character at end of parenthesized expression."), - } - - self.advance(); - - Ok(expr) - } - - /// Parses an expression that starts with an identifier (either a variable or a function call). - fn parse_id_expr(&mut self) -> Result { - let id = match self.curr() { - Ident(id) => id, - _ => return Err("Expected identifier."), - }; - - if self.advance().is_err() { - return Ok(Expr::Variable(id)); - } - - match self.curr() { - LParen => { - self.advance()?; - - if let RParen = self.curr() { - return Ok(Expr::Call { - fn_name: id, - args: vec![], - }); - } - - let mut args = vec![]; - - loop { - args.push(self.parse_expr()?); - - match self.current()? { - Comma => (), - RParen => break, - _ => return Err("Expected ',' character in function call."), - } - - self.advance()?; - } - - self.advance(); - - Ok(Expr::Call { fn_name: id, args }) - }, - - _ => Ok(Expr::Variable(id)), - } - } - - /// Parses an unary expression. - fn parse_unary_expr(&mut self) -> Result { - let op = match self.current()? { - Op(ch) => { - self.advance()?; - ch - }, - _ => return self.parse_primary(), - }; - - let mut name = String::from("unary"); - - name.push(op); - - Ok(Expr::Call { - fn_name: name, - args: vec![self.parse_unary_expr()?], - }) - } - - /// Parses a binary expression, given its left-hand expression. - fn parse_binary_expr(&mut self, prec: i32, mut left: Expr) -> Result { - loop { - let curr_prec = self.get_tok_precedence(); - - if curr_prec < prec || self.at_end() { - return Ok(left); - } - - let op = match self.curr() { - Op(op) => op, - _ => return Err("Invalid operator."), - }; - - self.advance()?; - - let mut right = self.parse_unary_expr()?; - - let next_prec = self.get_tok_precedence(); - - if curr_prec < next_prec { - right = self.parse_binary_expr(curr_prec + 1, right)?; - } - - left = Expr::Binary { - op, - left: Box::new(left), - right: Box::new(right), - }; - } - } - - /// Parses a conditional if..then..else expression. - fn parse_conditional_expr(&mut self) -> Result { - // eat 'if' token - self.advance()?; - - let cond = self.parse_expr()?; - - // eat 'then' token - match self.current() { - Ok(Then) => self.advance()?, - _ => return Err("Expected 'then' keyword."), - } - - let then = self.parse_expr()?; - - // eat 'else' token - match self.current() { - Ok(Else) => self.advance()?, - _ => return Err("Expected 'else' keyword."), - } - - let otherwise = self.parse_expr()?; - - Ok(Expr::Conditional { - cond: Box::new(cond), - consequence: Box::new(then), - alternative: Box::new(otherwise), - }) - } - - /// Parses a loop for..in.. expression. - fn parse_for_expr(&mut self) -> Result { - // eat 'for' token - self.advance()?; - - let name = match self.curr() { - Ident(n) => n, - _ => return Err("Expected identifier in for loop."), - }; - - // eat identifier - self.advance()?; - - // eat '=' token - match self.curr() { - Op('=') => self.advance()?, - _ => return Err("Expected '=' character in for loop."), - } - - let start = self.parse_expr()?; - - // eat ',' token - match self.current()? { - Comma => self.advance()?, - _ => return Err("Expected ',' character in for loop."), - } - - let end = self.parse_expr()?; - - // parse (optional) step expression - let step = match self.current()? { - Comma => { - self.advance()?; - - Some(self.parse_expr()?) - }, - - _ => None, - }; - - // eat 'in' token - match self.current()? { - In => self.advance()?, - _ => return Err("Expected 'in' keyword in for loop."), - } - - let body = self.parse_expr()?; - - Ok(Expr::For { - var_name: name, - start: Box::new(start), - end: Box::new(end), - step: step.map(Box::new), - body: Box::new(body), - }) - } - - /// Parses a var..in expression. - fn parse_var_expr(&mut self) -> Result { - // eat 'var' token - self.advance()?; - - let mut variables = Vec::new(); - - // parse variables - loop { - let name = match self.curr() { - Ident(name) => name, - _ => return Err("Expected identifier in 'var..in' declaration."), - }; - - self.advance()?; - - // read (optional) initializer - let initializer = match self.curr() { - Op('=') => Some({ - self.advance()?; - self.parse_expr()? - }), - - _ => None, - }; - - variables.push((name, initializer)); - - match self.curr() { - Comma => { - self.advance()?; - }, - In => { - self.advance()?; - break; - }, - _ => return Err("Expected comma or 'in' keyword in variable declaration."), - } - } - - // parse body - let body = self.parse_expr()?; - - Ok(Expr::VarIn { - variables, - body: Box::new(body), - }) - } - - /// Parses a primary expression (an identifier, a number or a parenthesized expression). - fn parse_primary(&mut self) -> Result { - match self.curr() { - Ident(_) => self.parse_id_expr(), - Number(_) => self.parse_nb_expr(), - LParen => self.parse_paren_expr(), - If => self.parse_conditional_expr(), - For => self.parse_for_expr(), - Var => self.parse_var_expr(), - _ => Err("Unknown expression."), - } - } - - /// Parses a top-level expression and makes an anonymous function out of it, - /// for easier compilation. - fn parse_toplevel_expr(&mut self) -> Result { - match self.parse_expr() { - Ok(expr) => Ok(Function { - prototype: Prototype { - name: ANONYMOUS_FUNCTION_NAME.to_string(), - args: vec![], - is_op: false, - prec: 0, - }, - body: Some(expr), - is_anon: true, - }), - - Err(err) => Err(err), - } - } -} - -// ====================================================================================== -// COMPILER ============================================================================= -// ====================================================================================== - -/// Defines the `Expr` compiler. -pub struct Compiler<'a, 'ctx> { - pub context: &'ctx Context, - pub builder: &'a Builder<'ctx>, - pub fpm: &'a PassManager>, - pub module: &'a Module<'ctx>, - pub function: &'a Function, - - variables: HashMap>, - fn_value_opt: Option>, -} - -impl<'a, 'ctx> Compiler<'a, 'ctx> { - /// Gets a defined function given its name. - #[inline] - fn get_function(&self, name: &str) -> Option> { - self.module.get_function(name) - } - - /// Returns the `FunctionValue` representing the function being compiled. - #[inline] - fn fn_value(&self) -> FunctionValue<'ctx> { - self.fn_value_opt.unwrap() - } - - /// Creates a new stack allocation instruction in the entry block of the function. - fn create_entry_block_alloca(&self, name: &str) -> PointerValue<'ctx> { - let builder = self.context.create_builder(); - - let entry = self.fn_value().get_first_basic_block().unwrap(); - - match entry.get_first_instruction() { - Some(first_instr) => builder.position_before(&first_instr), - None => builder.position_at_end(entry), - } - - builder.build_alloca(self.context.f64_type(), name) - } - - /// Compiles the specified `Expr` into an LLVM `FloatValue`. - fn compile_expr(&mut self, expr: &Expr) -> Result, &'static str> { - match *expr { - Expr::Number(nb) => Ok(self.context.f64_type().const_float(nb)), - - Expr::Variable(ref name) => match self.variables.get(name.as_str()) { - Some(var) => Ok(self.builder.build_load(*var, name.as_str()).into_float_value()), - None => Err("Could not find a matching variable."), - }, - - Expr::VarIn { - ref variables, - ref body, - } => { - let mut old_bindings = Vec::new(); - - for &(ref var_name, ref initializer) in variables { - let var_name = var_name.as_str(); - - let initial_val = match *initializer { - Some(ref init) => self.compile_expr(init)?, - None => self.context.f64_type().const_float(0.), - }; - - let alloca = self.create_entry_block_alloca(var_name); - - self.builder.build_store(alloca, initial_val); - - if let Some(old_binding) = self.variables.remove(var_name) { - old_bindings.push(old_binding); - } - - self.variables.insert(var_name.to_string(), alloca); - } - - let body = self.compile_expr(body)?; - - for binding in old_bindings { - self.variables - .insert(binding.get_name().to_str().unwrap().to_string(), binding); - } - - Ok(body) - }, - - Expr::Binary { - op, - ref left, - ref right, - } => { - if op == '=' { - // handle assignement - let var_name = match *left.borrow() { - Expr::Variable(ref var_name) => var_name, - _ => { - return Err("Expected variable as left-hand operator of assignement."); - }, - }; - - let var_val = self.compile_expr(right)?; - let var = self.variables.get(var_name.as_str()).ok_or("Undefined variable.")?; - - self.builder.build_store(*var, var_val); - - Ok(var_val) - } else { - let lhs = self.compile_expr(left)?; - let rhs = self.compile_expr(right)?; - - match op { - '+' => Ok(self.builder.build_float_add(lhs, rhs, "tmpadd")), - '-' => Ok(self.builder.build_float_sub(lhs, rhs, "tmpsub")), - '*' => Ok(self.builder.build_float_mul(lhs, rhs, "tmpmul")), - '/' => Ok(self.builder.build_float_div(lhs, rhs, "tmpdiv")), - '<' => Ok({ - let cmp = self - .builder - .build_float_compare(FloatPredicate::ULT, lhs, rhs, "tmpcmp"); - - self.builder - .build_unsigned_int_to_float(cmp, self.context.f64_type(), "tmpbool") - }), - '>' => Ok({ - let cmp = self - .builder - .build_float_compare(FloatPredicate::ULT, rhs, lhs, "tmpcmp"); - - self.builder - .build_unsigned_int_to_float(cmp, self.context.f64_type(), "tmpbool") - }), - - custom => { - let mut name = String::from("binary"); - - name.push(custom); - - match self.get_function(name.as_str()) { - Some(fun) => { - match self - .builder - .build_call(fun, &[lhs.into(), rhs.into()], "tmpbin") - .try_as_basic_value() - .left() - { - Some(value) => Ok(value.into_float_value()), - None => Err("Invalid call produced."), - } - }, - - None => Err("Undefined binary operator."), - } - }, - } - } - }, - - Expr::Call { ref fn_name, ref args } => match self.get_function(fn_name.as_str()) { - Some(fun) => { - let mut compiled_args = Vec::with_capacity(args.len()); - - for arg in args { - compiled_args.push(self.compile_expr(arg)?); - } - - let argsv: Vec = - compiled_args.iter().by_ref().map(|&val| val.into()).collect(); - - match self - .builder - .build_call(fun, argsv.as_slice(), "tmp") - .try_as_basic_value() - .left() - { - Some(value) => Ok(value.into_float_value()), - None => Err("Invalid call produced."), - } - }, - None => Err("Unknown function."), - }, - - Expr::Conditional { - ref cond, - ref consequence, - ref alternative, - } => { - let parent = self.fn_value(); - let zero_const = self.context.f64_type().const_float(0.0); - - // create condition by comparing without 0.0 and returning an int - let cond = self.compile_expr(cond)?; - let cond = self - .builder - .build_float_compare(FloatPredicate::ONE, cond, zero_const, "ifcond"); - - // build branch - let then_bb = self.context.append_basic_block(parent, "then"); - let else_bb = self.context.append_basic_block(parent, "else"); - let cont_bb = self.context.append_basic_block(parent, "ifcont"); - - self.builder.build_conditional_branch(cond, then_bb, else_bb); - - // build then block - self.builder.position_at_end(then_bb); - let then_val = self.compile_expr(consequence)?; - self.builder.build_unconditional_branch(cont_bb); - - let then_bb = self.builder.get_insert_block().unwrap(); - - // build else block - self.builder.position_at_end(else_bb); - let else_val = self.compile_expr(alternative)?; - self.builder.build_unconditional_branch(cont_bb); - - let else_bb = self.builder.get_insert_block().unwrap(); - - // emit merge block - self.builder.position_at_end(cont_bb); - - let phi = self.builder.build_phi(self.context.f64_type(), "iftmp"); - - phi.add_incoming(&[(&then_val, then_bb), (&else_val, else_bb)]); - - Ok(phi.as_basic_value().into_float_value()) - }, - - Expr::For { - ref var_name, - ref start, - ref end, - ref step, - ref body, - } => { - let parent = self.fn_value(); - - let start_alloca = self.create_entry_block_alloca(var_name); - let start = self.compile_expr(start)?; - - self.builder.build_store(start_alloca, start); - - // go from current block to loop block - let loop_bb = self.context.append_basic_block(parent, "loop"); - - self.builder.build_unconditional_branch(loop_bb); - self.builder.position_at_end(loop_bb); - - let old_val = self.variables.remove(var_name.as_str()); - - self.variables.insert(var_name.to_owned(), start_alloca); - - // emit body - self.compile_expr(body)?; - - // emit step - let step = match *step { - Some(ref step) => self.compile_expr(step)?, - None => self.context.f64_type().const_float(1.0), - }; - - // compile end condition - let end_cond = self.compile_expr(end)?; - - let curr_var = self.builder.build_load(start_alloca, var_name); - let next_var = self - .builder - .build_float_add(curr_var.into_float_value(), step, "nextvar"); - - self.builder.build_store(start_alloca, next_var); - - let end_cond = self.builder.build_float_compare( - FloatPredicate::ONE, - end_cond, - self.context.f64_type().const_float(0.0), - "loopcond", - ); - let after_bb = self.context.append_basic_block(parent, "afterloop"); - - self.builder.build_conditional_branch(end_cond, loop_bb, after_bb); - self.builder.position_at_end(after_bb); - - self.variables.remove(var_name); - - if let Some(val) = old_val { - self.variables.insert(var_name.to_owned(), val); - } - - Ok(self.context.f64_type().const_float(0.0)) - }, - } - } - - /// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`. - fn compile_prototype(&self, proto: &Prototype) -> Result, &'static str> { - let ret_type = self.context.f64_type(); - let args_types = std::iter::repeat(ret_type) - .take(proto.args.len()) - .map(|f| f.into()) - .collect::>(); - let args_types = args_types.as_slice(); - - let fn_type = self.context.f64_type().fn_type(args_types, false); - let fn_val = self.module.add_function(proto.name.as_str(), fn_type, None); - - // set arguments names - for (i, arg) in fn_val.get_param_iter().enumerate() { - arg.into_float_value().set_name(proto.args[i].as_str()); - } - - // finally return built prototype - Ok(fn_val) - } - - /// Compiles the specified `Function` into an LLVM `FunctionValue`. - fn compile_fn(&mut self) -> Result, &'static str> { - let proto = &self.function.prototype; - let function = self.compile_prototype(proto)?; - - // got external function, returning only compiled prototype - if self.function.body.is_none() { - return Ok(function); - } - - let entry = self.context.append_basic_block(function, "entry"); - - self.builder.position_at_end(entry); - - // update fn field - self.fn_value_opt = Some(function); - - // build variables map - self.variables.reserve(proto.args.len()); - - for (i, arg) in function.get_param_iter().enumerate() { - let arg_name = proto.args[i].as_str(); - let alloca = self.create_entry_block_alloca(arg_name); - - self.builder.build_store(alloca, arg); - - self.variables.insert(proto.args[i].clone(), alloca); - } - - // compile body - let body = self.compile_expr(self.function.body.as_ref().unwrap())?; - - self.builder.build_return(Some(&body)); - - // return the whole thing after verification and optimization - if function.verify(true) { - self.fpm.run_on(&function); - - Ok(function) - } else { - unsafe { - function.delete(); - } - - Err("Invalid generated function.") - } - } - - /// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`. - pub fn compile( - context: &'ctx Context, - builder: &'a Builder<'ctx>, - pass_manager: &'a PassManager>, - module: &'a Module<'ctx>, - function: &Function, - ) -> Result, &'static str> { - let mut compiler = Compiler { - context, - builder, - fpm: pass_manager, - module, - function, - fn_value_opt: None, - variables: HashMap::new(), - }; - - compiler.compile_fn() - } -} +#[llvm_versions(4.0..=14.0)] +use crate::implementation_typed_pointers::*; // ====================================================================================== // PROGRAM ============================================================================== @@ -1243,6 +58,7 @@ pub extern "C" fn printd(x: f64) -> f64 { static EXTERNAL_FNS: [extern "C" fn(f64) -> f64; 2] = [putchard, printd]; /// Entry point of the program; acts as a REPL. +#[llvm_versions(4.0..=14.0)] pub fn main() { // use self::inkwell::support::add_symbol; let mut display_lexer_output = false; @@ -1379,3 +195,8 @@ pub fn main() { } } } + +#[llvm_versions(15.0..=latest)] +pub fn main() { + eprintln!("Kaleidoscope example does not work yet with this llvm version"); +} diff --git a/internal_macros/src/lib.rs b/internal_macros/src/lib.rs index 7e99f343105..95f602abfed 100644 --- a/internal_macros/src/lib.rs +++ b/internal_macros/src/lib.rs @@ -12,9 +12,9 @@ use syn::{parenthesized, parse_macro_input, parse_quote}; use syn::{Attribute, Field, Ident, Item, LitFloat, Token, Variant}; // This array should match the LLVM features in the top level Cargo manifest -const FEATURE_VERSIONS: [&str; 11] = [ +const FEATURE_VERSIONS: [&str; 12] = [ "llvm4-0", "llvm5-0", "llvm6-0", "llvm7-0", "llvm8-0", "llvm9-0", "llvm10-0", "llvm11-0", "llvm12-0", "llvm13-0", - "llvm14-0", + "llvm14-0", "llvm15-0", ]; /// Gets the index of the feature version that represents `latest` diff --git a/src/attributes.rs b/src/attributes.rs index 8a8abfaab1f..c2587434ad8 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -121,7 +121,7 @@ impl Attribute { /// /// assert_eq!(enum_attribute.get_enum_kind_id(), 0); /// ``` - #[llvm_versions(4.0..12.0)] + #[llvm_versions(4.0..=11.0)] pub fn get_enum_kind_id(self) -> u32 { assert!(self.get_enum_kind_id_is_valid()); // FIXME: SubTypes @@ -165,7 +165,7 @@ impl Attribute { unsafe { LLVMGetEnumAttributeKind(self.attribute) } } - #[llvm_versions(4.0..12.0)] + #[llvm_versions(4.0..=11.0)] fn get_enum_kind_id_is_valid(self) -> bool { self.is_enum() } diff --git a/src/basic_block.rs b/src/basic_block.rs index ff302063dd7..8e75e672243 100644 --- a/src/basic_block.rs +++ b/src/basic_block.rs @@ -5,7 +5,7 @@ use llvm_sys::core::{ LLVMGetBasicBlockTerminator, LLVMGetFirstInstruction, LLVMGetFirstUse, LLVMGetLastInstruction, LLVMGetNextBasicBlock, LLVMGetPreviousBasicBlock, LLVMGetTypeContext, LLVMIsABasicBlock, LLVMIsConstant, LLVMMoveBasicBlockAfter, LLVMMoveBasicBlockBefore, LLVMPrintTypeToString, LLVMPrintValueToString, - LLVMRemoveBasicBlockFromParent, LLVMReplaceAllUsesWith, LLVMSetValueName, LLVMTypeOf, + LLVMRemoveBasicBlockFromParent, LLVMReplaceAllUsesWith, LLVMTypeOf, }; use llvm_sys::prelude::{LLVMBasicBlockRef, LLVMValueRef}; @@ -424,7 +424,19 @@ impl<'ctx> BasicBlock<'ctx> { /// Set name of the `BasicBlock`. pub fn set_name(&self, name: &str) { let c_string = to_c_str(name); - unsafe { LLVMSetValueName(LLVMBasicBlockAsValue(self.basic_block), c_string.as_ptr()) }; + + #[cfg(any(feature = "llvm4-0", feature = "llvm5-0", feature = "llvm6-0"))] + { + use llvm_sys::core::LLVMSetValueName; + + unsafe { LLVMSetValueName(LLVMBasicBlockAsValue(self.basic_block), c_string.as_ptr()) }; + } + #[cfg(not(any(feature = "llvm4-0", feature = "llvm5-0", feature = "llvm6-0")))] + { + use llvm_sys::core::LLVMSetValueName2; + + unsafe { LLVMSetValueName2(LLVMBasicBlockAsValue(self.basic_block), c_string.as_ptr(), name.len()) }; + } } /// Replaces all uses of this basic block with another. diff --git a/src/builder.rs b/src/builder.rs index dbd710cad1c..319999515ae 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -19,12 +19,12 @@ use llvm_sys::core::{ LLVMInsertIntoBuilderWithName, LLVMPositionBuilder, LLVMPositionBuilderAtEnd, LLVMPositionBuilderBefore, LLVMSetCleanup, }; -#[llvm_versions(4.0..14.0)] +#[llvm_versions(4.0..=14.0)] use llvm_sys::core::{ LLVMBuildCall, LLVMBuildGEP, LLVMBuildInBoundsGEP, LLVMBuildInvoke, LLVMBuildLoad, LLVMBuildPtrDiff, LLVMBuildStructGEP, }; -#[llvm_versions(14.0..=latest)] +#[llvm_versions(15.0..=latest)] use llvm_sys::core::{ LLVMBuildCall2, LLVMBuildGEP2, LLVMBuildInBoundsGEP2, LLVMBuildInvoke2, LLVMBuildLoad2, LLVMBuildPtrDiff2, LLVMBuildStructGEP2, @@ -35,17 +35,20 @@ use llvm_sys::core::{LLVMBuildIntCast2, LLVMBuildMemCpy, LLVMBuildMemMove, LLVMB use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef}; use crate::basic_block::BasicBlock; -#[llvm_versions(7.0..=latest)] +#[llvm_versions(7.0..=8.0)] use crate::context::AsContextRef; #[llvm_versions(7.0..=latest)] use crate::debug_info::DILocation; use crate::support::to_c_str; -use crate::types::{AsTypeRef, BasicType, FloatMathType, IntMathType, PointerMathType, PointerType}; +use crate::types::{AsTypeRef, BasicType, FloatMathType, FunctionType, IntMathType, PointerMathType, PointerType}; +#[llvm_versions(4.0..=14.0)] +use crate::values::CallableValue; use crate::values::{ AggregateValue, AggregateValueEnum, AsValueRef, BasicMetadataValueEnum, BasicValue, BasicValueEnum, CallSiteValue, - CallableValue, FloatMathValue, FunctionValue, GlobalValue, InstructionOpcode, InstructionValue, IntMathValue, - IntValue, PhiValue, PointerMathValue, PointerValue, StructValue, VectorValue, + FloatMathValue, FunctionValue, GlobalValue, InstructionOpcode, InstructionValue, IntMathValue, IntValue, PhiValue, + PointerMathValue, PointerValue, StructValue, VectorValue, }; + #[cfg(feature = "internal-getters")] use crate::LLVMReference; use crate::{AtomicOrdering, AtomicRMWBinOp, FloatPredicate, IntPredicate}; @@ -58,6 +61,9 @@ pub struct Builder<'ctx> { _marker: PhantomData<&'ctx ()>, } +#[allow(unused)] // only used in documentation +use crate::context::Context; + impl<'ctx> Builder<'ctx> { pub(crate) unsafe fn new(builder: LLVMBuilderRef) -> Self { debug_assert!(!builder.is_null()); @@ -164,6 +170,7 @@ impl<'ctx> Builder<'ctx> { /// /// builder.build_return(Some(&ret_val)); /// ``` + #[llvm_versions(4.0..=14.0)] pub fn build_call(&self, function: F, args: &[BasicMetadataValueEnum<'ctx>], name: &str) -> CallSiteValue<'ctx> where F: Into>, @@ -177,23 +184,9 @@ impl<'ctx> Builder<'ctx> { let c_string = to_c_str(name); let mut args: Vec = args.iter().map(|val| val.as_value_ref()).collect(); - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] let value = unsafe { - let fn_ty_ref = callable_value.as_type_ref(); - LLVMBuildCall2( + LLVMBuildCall( self.builder, - fn_ty_ref, fn_val_ref, args.as_mut_ptr(), args.len() as u32, @@ -201,21 +194,122 @@ impl<'ctx> Builder<'ctx> { ) }; - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] + unsafe { CallSiteValue::new(value) } + } + + /// Builds a function call instruction. Alias for [Builder::build_direct_call]. + #[llvm_versions(15.0..=latest)] + pub fn build_call( + &self, + function: FunctionValue<'ctx>, + args: &[BasicMetadataValueEnum<'ctx>], + name: &str, + ) -> CallSiteValue<'ctx> { + self.build_direct_call(function, args, name) + } + + /// Builds a function call instruction. The function being called is known at compile time. If + /// you want to call a function pointer, see [Builder::build_indirect_call]. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// + /// // A simple function which calls itself: + /// let context = Context::create(); + /// let module = context.create_module("ret"); + /// let builder = context.create_builder(); + /// let i32_type = context.i32_type(); + /// let fn_type = i32_type.fn_type(&[i32_type.into()], false); + /// let fn_value = module.add_function("ret", fn_type, None); + /// let entry = context.append_basic_block(fn_value, "entry"); + /// let i32_arg = fn_value.get_first_param().unwrap(); + /// let md_string = context.metadata_string("a metadata"); + /// + /// builder.position_at_end(entry); + /// + /// let ret_val = builder.build_call(fn_value, &[i32_arg.into(), md_string.into()], "call") + /// .try_as_basic_value() + /// .left() + /// .unwrap(); + /// + /// builder.build_return(Some(&ret_val)); + /// ``` + #[llvm_versions(15.0..=latest)] + pub fn build_direct_call( + &self, + function: FunctionValue<'ctx>, + args: &[BasicMetadataValueEnum<'ctx>], + name: &str, + ) -> CallSiteValue<'ctx> { + self.build_call_help(function.get_type(), function.as_value_ref(), args, name) + } + + /// Call a function pointer. Because a pointer does not carry a type, the type of the function + /// must be specified explicitly. + /// + /// See [Context::create_inline_asm] for a practical example. Basic usage looks like this: + /// + /// ```no_run + /// use inkwell::context::Context; + /// + /// // A simple function which calls itself: + /// let context = Context::create(); + /// let module = context.create_module("ret"); + /// let builder = context.create_builder(); + /// let i32_type = context.i32_type(); + /// let fn_type = i32_type.fn_type(&[i32_type.into()], false); + /// let fn_value = module.add_function("ret", fn_type, None); + /// let entry = context.append_basic_block(fn_value, "entry"); + /// let i32_arg = fn_value.get_first_param().unwrap(); + /// let md_string = context.metadata_string("a metadata"); + /// + /// builder.position_at_end(entry); + /// + /// let function_pointer = fn_value.as_global_value().as_pointer_value(); + /// let ret_val = builder.build_indirect_call(fn_value.get_type(), function_pointer, &[i32_arg.into(), md_string.into()], "call") + /// .try_as_basic_value() + /// .left() + /// .unwrap(); + /// + /// builder.build_return(Some(&ret_val)); + /// ``` + /// + #[llvm_versions(15.0..=latest)] + pub fn build_indirect_call( + &self, + function_type: FunctionType<'ctx>, + function_pointer: PointerValue<'ctx>, + args: &[BasicMetadataValueEnum<'ctx>], + name: &str, + ) -> CallSiteValue<'ctx> { + self.build_call_help(function_type, function_pointer.as_value_ref(), args, name) + } + + #[llvm_versions(15.0..=latest)] + fn build_call_help( + &self, + function_type: FunctionType<'ctx>, + fn_val_ref: LLVMValueRef, + args: &[BasicMetadataValueEnum<'ctx>], + name: &str, + ) -> CallSiteValue<'ctx> { + // LLVM gets upset when void return calls are named because they don't return anything + let name = match function_type.get_return_type() { + None => "", + Some(_) => name, + }; + + let fn_ty_ref = function_type.as_type_ref(); + + let c_string = to_c_str(name); + let mut args: Vec = args.iter().map(|val| val.as_value_ref()).collect(); + let value = unsafe { - LLVMBuildCall( + LLVMBuildCall2( self.builder, + fn_ty_ref, fn_val_ref, args.as_mut_ptr(), args.len() as u32, @@ -305,6 +399,7 @@ impl<'ctx> Builder<'ctx> { /// builder.build_return(Some(&f32_type.const_zero())); /// } /// ``` + #[llvm_versions(4.0..=14.0)] pub fn build_invoke( &self, function: F, @@ -325,25 +420,9 @@ impl<'ctx> Builder<'ctx> { let c_string = to_c_str(name); let mut args: Vec = args.iter().map(|val| val.as_value_ref()).collect(); - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] let value = unsafe { - let fn_ty_ref = callable_value.as_type_ref(); - LLVMBuildInvoke2( + LLVMBuildInvoke( self.builder, - fn_ty_ref, fn_val_ref, args.as_mut_ptr(), args.len() as u32, @@ -353,21 +432,161 @@ impl<'ctx> Builder<'ctx> { ) }; - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] + unsafe { CallSiteValue::new(value) } + } + + /// An invoke is similar to a normal function call, but used to + /// call functions that may throw an exception, and then respond to the exception. + /// + /// When the called function returns normally, the `then` block is evaluated next. If instead + /// the function threw an exception, the `catch` block is entered. The first non-phi + /// instruction of the catch block must be a `landingpad` instruction. See also + /// [`Builder::build_landing_pad`]. + /// + /// The [`add_prune_eh_pass`] turns an invoke into a call when the called function is + /// guaranteed to never throw an exception. + /// + /// [`add_prune_eh_pass`]: crate::passes::PassManager::add_prune_eh_pass + /// + /// This example catches C++ exceptions of type `int`, and returns `0` if an exceptions is thrown. + /// For usage of a cleanup landing pad and the `resume` instruction, see [`Builder::build_resume`] + /// ```no_run + /// use inkwell::context::Context; + /// use inkwell::AddressSpace; + /// use inkwell::module::Linkage; + /// + /// let context = Context::create(); + /// let module = context.create_module("sum"); + /// let builder = context.create_builder(); + /// + /// let f32_type = context.f32_type(); + /// let fn_type = f32_type.fn_type(&[], false); + /// + /// // we will pretend this function can throw an exception + /// let function = module.add_function("bomb", fn_type, None); + /// let basic_block = context.append_basic_block(function, "entry"); + /// + /// builder.position_at_end(basic_block); + /// + /// let pi = f32_type.const_float(::std::f64::consts::PI); + /// + /// builder.build_return(Some(&pi)); + /// + /// let function2 = module.add_function("wrapper", fn_type, None); + /// let basic_block2 = context.append_basic_block(function2, "entry"); + /// + /// builder.position_at_end(basic_block2); + /// + /// let then_block = context.append_basic_block(function2, "then_block"); + /// let catch_block = context.append_basic_block(function2, "catch_block"); + /// + /// let call_site = builder.build_invoke(function, &[], then_block, catch_block, "get_pi"); + /// + /// { + /// builder.position_at_end(then_block); + /// + /// // in the then_block, the `call_site` value is defined and can be used + /// let result = call_site.try_as_basic_value().left().unwrap(); + /// + /// builder.build_return(Some(&result)); + /// } + /// + /// { + /// builder.position_at_end(catch_block); + /// + /// // the personality function used by C++ + /// let personality_function = { + /// let name = "__gxx_personality_v0"; + /// let linkage = Some(Linkage::External); + /// + /// module.add_function(name, context.i64_type().fn_type(&[], false), linkage) + /// }; + /// + /// // type of an exception in C++ + /// let i8_ptr_type = context.i32_type().ptr_type(AddressSpace::default()); + /// let i32_type = context.i32_type(); + /// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false); + /// + /// let null = i8_ptr_type.const_zero(); + /// let res = builder.build_landing_pad(exception_type, personality_function, &[null.into()], false, "res"); + /// + /// // we handle the exception by returning a default value + /// builder.build_return(Some(&f32_type.const_zero())); + /// } + /// ``` + #[llvm_versions(15.0..=latest)] + pub fn build_invoke( + &self, + function: FunctionValue<'ctx>, + args: &[BasicValueEnum<'ctx>], + then_block: BasicBlock<'ctx>, + catch_block: BasicBlock<'ctx>, + name: &str, + ) -> CallSiteValue<'ctx> { + self.build_direct_invoke(function, args, then_block, catch_block, name) + } + + #[llvm_versions(15.0..=latest)] + pub fn build_direct_invoke( + &self, + function: FunctionValue<'ctx>, + args: &[BasicValueEnum<'ctx>], + then_block: BasicBlock<'ctx>, + catch_block: BasicBlock<'ctx>, + name: &str, + ) -> CallSiteValue<'ctx> { + self.build_invoke_help( + function.get_type(), + function.as_value_ref(), + args, + then_block, + catch_block, + name, + ) + } + + #[llvm_versions(15.0..=latest)] + pub fn build_indirect_invoke( + &self, + function_type: FunctionType<'ctx>, + function_pointer: PointerValue<'ctx>, + args: &[BasicValueEnum<'ctx>], + then_block: BasicBlock<'ctx>, + catch_block: BasicBlock<'ctx>, + name: &str, + ) -> CallSiteValue<'ctx> { + self.build_invoke_help( + function_type, + function_pointer.as_value_ref(), + args, + then_block, + catch_block, + name, + ) + } + + #[llvm_versions(15.0..=latest)] + fn build_invoke_help( + &self, + fn_ty: FunctionType<'ctx>, + fn_val_ref: LLVMValueRef, + args: &[BasicValueEnum<'ctx>], + then_block: BasicBlock<'ctx>, + catch_block: BasicBlock<'ctx>, + name: &str, + ) -> CallSiteValue<'ctx> { + let fn_ty_ref = fn_ty.as_type_ref(); + + // LLVM gets upset when void return calls are named because they don't return anything + let name = if fn_ty.get_return_type().is_none() { "" } else { name }; + + let c_string = to_c_str(name); + let mut args: Vec = args.iter().map(|val| val.as_value_ref()).collect(); + let value = unsafe { - LLVMBuildInvoke( + LLVMBuildInvoke2( self.builder, + fn_ty_ref, fn_val_ref, args.as_mut_ptr(), args.len() as u32, @@ -635,6 +854,7 @@ impl<'ctx> Builder<'ctx> { // REVIEW: Doesn't GEP work on array too? /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(4.0..=14.0)] pub unsafe fn build_gep( &self, ptr: PointerValue<'ctx>, @@ -645,43 +865,34 @@ impl<'ctx> Builder<'ctx> { let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] - let value = LLVMBuildGEP2( + let value = LLVMBuildGEP( self.builder, - ptr.get_type().get_element_type().as_type_ref(), ptr.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, c_string.as_ptr(), ); - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] - let value = LLVMBuildGEP( + PointerValue::new(value) + } + + // REVIEW: Doesn't GEP work on array too? + /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(15.0..=latest)] + pub unsafe fn build_gep>( + &self, + pointee_ty: T, + ptr: PointerValue<'ctx>, + ordered_indexes: &[IntValue<'ctx>], + name: &str, + ) -> PointerValue<'ctx> { + let c_string = to_c_str(name); + + let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); + + let value = LLVMBuildGEP2( self.builder, + pointee_ty.as_type_ref(), ptr.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, @@ -694,6 +905,7 @@ impl<'ctx> Builder<'ctx> { // REVIEW: Doesn't GEP work on array too? // REVIEW: This could be merge in with build_gep via a in_bounds: bool param /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(4.0..=14.0)] pub unsafe fn build_in_bounds_gep( &self, ptr: PointerValue<'ctx>, @@ -704,43 +916,35 @@ impl<'ctx> Builder<'ctx> { let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] - let value = LLVMBuildInBoundsGEP2( + let value = LLVMBuildInBoundsGEP( self.builder, - ptr.get_type().get_element_type().as_type_ref(), ptr.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, c_string.as_ptr(), ); - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] - let value = LLVMBuildInBoundsGEP( + PointerValue::new(value) + } + + // REVIEW: Doesn't GEP work on array too? + // REVIEW: This could be merge in with build_gep via a in_bounds: bool param + /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(15.0..=latest)] + pub unsafe fn build_in_bounds_gep>( + &self, + pointee_ty: T, + ptr: PointerValue<'ctx>, + ordered_indexes: &[IntValue<'ctx>], + name: &str, + ) -> PointerValue<'ctx> { + let c_string = to_c_str(name); + + let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); + + let value = LLVMBuildInBoundsGEP2( self.builder, + pointee_ty.as_type_ref(), ptr.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, @@ -783,6 +987,7 @@ impl<'ctx> Builder<'ctx> { /// assert!(builder.build_struct_gep(struct_ptr, 1, "struct_gep").is_ok()); /// assert!(builder.build_struct_gep(struct_ptr, 2, "struct_gep").is_err()); /// ``` + #[llvm_versions(4.0..=14.0)] pub fn build_struct_gep(&self, ptr: PointerValue<'ctx>, index: u32, name: &str) -> Result, ()> { let ptr_ty = ptr.get_type(); let pointee_ty = ptr_ty.get_element_type(); @@ -799,44 +1004,76 @@ impl<'ctx> Builder<'ctx> { let c_string = to_c_str(name); - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] + let value = unsafe { LLVMBuildStructGEP(self.builder, ptr.as_value_ref(), index, c_string.as_ptr()) }; + + unsafe { Ok(PointerValue::new(value)) } + } + + /// Builds a GEP instruction on a struct pointer. Returns `Err(())` if input `PointerValue` doesn't + /// point to a struct or if index is out of bounds. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::AddressSpace; + /// use inkwell::context::Context; + /// + /// let context = Context::create(); + /// let builder = context.create_builder(); + /// let module = context.create_module("struct_gep"); + /// let void_type = context.void_type(); + /// let i32_ty = context.i32_type(); + /// let i32_ptr_ty = i32_ty.ptr_type(AddressSpace::default()); + /// let field_types = &[i32_ty.into(), i32_ty.into()]; + /// let struct_ty = context.struct_type(field_types, false); + /// let struct_ptr_ty = struct_ty.ptr_type(AddressSpace::default()); + /// let fn_type = void_type.fn_type(&[i32_ptr_ty.into(), struct_ptr_ty.into()], false); + /// let fn_value = module.add_function("", fn_type, None); + /// let entry = context.append_basic_block(fn_value, "entry"); + /// + /// builder.position_at_end(entry); + /// + /// let i32_ptr = fn_value.get_first_param().unwrap().into_pointer_value(); + /// let struct_ptr = fn_value.get_last_param().unwrap().into_pointer_value(); + /// + /// assert!(builder.build_struct_gep(i32_ty, i32_ptr, 0, "struct_gep").is_err()); + /// assert!(builder.build_struct_gep(i32_ty, i32_ptr, 10, "struct_gep").is_err()); + /// assert!(builder.build_struct_gep(struct_ty, struct_ptr, 0, "struct_gep").is_ok()); + /// assert!(builder.build_struct_gep(struct_ty, struct_ptr, 1, "struct_gep").is_ok()); + /// assert!(builder.build_struct_gep(struct_ty, struct_ptr, 2, "struct_gep").is_err()); + /// ``` + #[llvm_versions(15.0..=latest)] + pub fn build_struct_gep>( + &self, + pointee_ty: T, + ptr: PointerValue<'ctx>, + index: u32, + name: &str, + ) -> Result, ()> { + let pointee_ty = pointee_ty.as_any_type_enum(); + + if !pointee_ty.is_struct_type() { + return Err(()); + } + + let struct_ty = pointee_ty.into_struct_type(); + + if index >= struct_ty.count_fields() { + return Err(()); + } + + let c_string = to_c_str(name); + let value = unsafe { LLVMBuildStructGEP2( self.builder, - ptr.get_type().get_element_type().as_type_ref(), + pointee_ty.as_type_ref(), ptr.as_value_ref(), index, c_string.as_ptr(), ) }; - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] - let value = unsafe { LLVMBuildStructGEP(self.builder, ptr.as_value_ref(), index, c_string.as_ptr()) }; - unsafe { Ok(PointerValue::new(value)) } } @@ -865,6 +1102,7 @@ impl<'ctx> Builder<'ctx> { /// builder.build_ptr_diff(i32_ptr_param1, i32_ptr_param2, "diff"); /// builder.build_return(None); /// ``` + #[llvm_versions(4.0..=14.0)] pub fn build_ptr_diff( &self, lhs_ptr: PointerValue<'ctx>, @@ -872,46 +1110,57 @@ impl<'ctx> Builder<'ctx> { name: &str, ) -> IntValue<'ctx> { let c_string = to_c_str(name); - - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] let value = unsafe { - LLVMBuildPtrDiff2( + LLVMBuildPtrDiff( self.builder, - lhs_ptr.get_type().as_type_ref(), lhs_ptr.as_value_ref(), rhs_ptr.as_value_ref(), c_string.as_ptr(), ) }; - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] + unsafe { IntValue::new(value) } + } + + /// Builds an instruction which calculates the difference of two pointers. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// use inkwell::AddressSpace; + /// + /// // Builds a function which diffs two pointers + /// let context = Context::create(); + /// let module = context.create_module("ret"); + /// let builder = context.create_builder(); + /// let void_type = context.void_type(); + /// let i32_type = context.i32_type(); + /// let i32_ptr_type = i32_type.ptr_type(AddressSpace::default()); + /// let fn_type = void_type.fn_type(&[i32_ptr_type.into(), i32_ptr_type.into()], false); + /// let fn_value = module.add_function("ret", fn_type, None); + /// let entry = context.append_basic_block(fn_value, "entry"); + /// let i32_ptr_param1 = fn_value.get_first_param().unwrap().into_pointer_value(); + /// let i32_ptr_param2 = fn_value.get_nth_param(1).unwrap().into_pointer_value(); + /// + /// builder.position_at_end(entry); + /// builder.build_ptr_diff(i32_ptr_type, i32_ptr_param1, i32_ptr_param2, "diff"); + /// builder.build_return(None); + /// ``` + #[llvm_versions(15.0..=latest)] + pub fn build_ptr_diff>( + &self, + pointee_ty: T, + lhs_ptr: PointerValue<'ctx>, + rhs_ptr: PointerValue<'ctx>, + name: &str, + ) -> IntValue<'ctx> { + let c_string = to_c_str(name); + let value = unsafe { - LLVMBuildPtrDiff( + LLVMBuildPtrDiff2( self.builder, + pointee_ty.as_type_ref(), lhs_ptr.as_value_ref(), rhs_ptr.as_value_ref(), c_string.as_ptr(), @@ -989,46 +1238,58 @@ impl<'ctx> Builder<'ctx> { /// /// builder.build_return(Some(&pointee)); /// ``` + #[llvm_versions(4.0..=14.0)] pub fn build_load(&self, ptr: PointerValue<'ctx>, name: &str) -> BasicValueEnum<'ctx> { let c_string = to_c_str(name); - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] + let value = unsafe { LLVMBuildLoad(self.builder, ptr.as_value_ref(), c_string.as_ptr()) }; + + unsafe { BasicValueEnum::new(value) } + } + + /// Builds a load2 instruction. It allows you to retrieve a value of type `T` from a pointer to a type `T`. + /// + /// # Example + /// + /// ```no_run + /// use inkwell::context::Context; + /// use inkwell::AddressSpace; + /// + /// // Builds a function which takes an i32 pointer and returns the pointed at i32. + /// let context = Context::create(); + /// let module = context.create_module("ret"); + /// let builder = context.create_builder(); + /// let i32_type = context.i32_type(); + /// let i32_ptr_type = i32_type.ptr_type(AddressSpace::default()); + /// let fn_type = i32_type.fn_type(&[i32_ptr_type.into()], false); + /// let fn_value = module.add_function("ret", fn_type, None); + /// let entry = context.append_basic_block(fn_value, "entry"); + /// let i32_ptr_param = fn_value.get_first_param().unwrap().into_pointer_value(); + /// + /// builder.position_at_end(entry); + /// + /// let pointee = builder.build_load(i32_type, i32_ptr_param, "load2"); + /// + /// builder.build_return(Some(&pointee)); + /// ``` + #[llvm_versions(15.0..=latest)] + pub fn build_load>( + &self, + pointee_ty: T, + ptr: PointerValue<'ctx>, + name: &str, + ) -> BasicValueEnum<'ctx> { + let c_string = to_c_str(name); + let value = unsafe { LLVMBuildLoad2( self.builder, - ptr.get_type().get_element_type().as_type_ref(), + pointee_ty.as_type_ref(), ptr.as_value_ref(), c_string.as_ptr(), ) }; - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] - let value = unsafe { LLVMBuildLoad(self.builder, ptr.as_value_ref(), c_string.as_ptr()) }; - unsafe { BasicValueEnum::new(value) } } @@ -1314,6 +1575,7 @@ impl<'ctx> Builder<'ctx> { /// # Example /// /// ```no_run + /// use inkwell::AddressSpace; /// use inkwell::context::Context; /// /// let context = Context::create(); @@ -1341,6 +1603,7 @@ impl<'ctx> Builder<'ctx> { V: BasicValue<'ctx>, { let c_string = to_c_str(name); + let value = unsafe { LLVMBuildBitCast(self.builder, val.as_value_ref(), ty.as_type_ref(), c_string.as_ptr()) }; unsafe { BasicValueEnum::new(value) } @@ -2086,7 +2349,12 @@ impl<'ctx> Builder<'ctx> { /// builder.position_at_end(entry); /// /// let array_alloca = builder.build_alloca(array_type, "array_alloca"); + /// + /// #[cfg(not(any(feature = "llvm15-0")))] /// let array = builder.build_load(array_alloca, "array_load").into_array_value(); + /// #[cfg(any(feature = "llvm15-0"))] + /// let array = builder.build_load(i32_type, array_alloca, "array_load").into_array_value(); + /// /// let const_int1 = i32_type.const_int(2, false); /// let const_int2 = i32_type.const_int(5, false); /// let const_int3 = i32_type.const_int(6, false); @@ -2146,7 +2414,12 @@ impl<'ctx> Builder<'ctx> { /// builder.position_at_end(entry); /// /// let array_alloca = builder.build_alloca(array_type, "array_alloca"); + /// + /// #[cfg(not(any(feature = "llvm15-0")))] /// let array = builder.build_load(array_alloca, "array_load").into_array_value(); + /// #[cfg(any(feature = "llvm15-0"))] + /// let array = builder.build_load(i32_type, array_alloca, "array_load").into_array_value(); + /// /// let const_int1 = i32_type.const_int(2, false); /// let const_int2 = i32_type.const_int(5, false); /// let const_int3 = i32_type.const_int(6, false); @@ -2517,6 +2790,8 @@ impl<'ctx> Builder<'ctx> { if value.get_type().get_bit_width() < 8 || !value.get_type().get_bit_width().is_power_of_two() { return Err("The bitwidth of value must be a power of 2 and greater than 8."); } + + #[cfg(not(any(feature = "llvm15-0")))] if ptr.get_type().get_element_type() != value.get_type().into() { return Err("Pointer's pointee type must match the value's type."); } @@ -2575,6 +2850,8 @@ impl<'ctx> Builder<'ctx> { if !cmp.is_int_value() && !cmp.is_pointer_value() { return Err("The values must have pointer or integer type."); } + + #[cfg(not(any(feature = "llvm15-0")))] if ptr.get_type().get_element_type().to_basic_type_enum() != cmp.get_type() { return Err("The pointer does not point to an element of the value type."); } @@ -2606,7 +2883,7 @@ impl<'ctx> Builder<'ctx> { } /// Set the debug info source location of the instruction currently pointed at by the builder - #[llvm_versions(7.0..=latest)] + #[llvm_versions(7.0..=8.0)] pub fn set_current_debug_location(&self, context: impl AsContextRef<'ctx>, location: DILocation<'ctx>) { use llvm_sys::core::LLVMMetadataAsValue; use llvm_sys::core::LLVMSetCurrentDebugLocation; @@ -2618,6 +2895,15 @@ impl<'ctx> Builder<'ctx> { } } + /// Set the debug info source location of the instruction currently pointed at by the builder + #[llvm_versions(9.0..=latest)] + pub fn set_current_debug_location(&self, location: DILocation<'ctx>) { + use llvm_sys::core::LLVMSetCurrentDebugLocation2; + unsafe { + LLVMSetCurrentDebugLocation2(self.builder, location.metadata_ref); + } + } + /// Get the debug info source location of the instruction currently pointed at by the builder, /// if available. #[llvm_versions(7.0..=latest)] @@ -2636,12 +2922,23 @@ impl<'ctx> Builder<'ctx> { /// Unset the debug info source location of the instruction currently pointed at by the /// builder. If there isn't any debug info, this is a no-op. + #[llvm_versions(7.0..=8.0)] pub fn unset_current_debug_location(&self) { use llvm_sys::core::LLVMSetCurrentDebugLocation; unsafe { LLVMSetCurrentDebugLocation(self.builder, std::ptr::null_mut()); } } + + /// Unset the debug info source location of the instruction currently pointed at by the + /// builder. If there isn't any debug info, this is a no-op. + #[llvm_versions(9.0..=latest)] + pub fn unset_current_debug_location(&self) { + use llvm_sys::core::LLVMSetCurrentDebugLocation2; + unsafe { + LLVMSetCurrentDebugLocation2(self.builder, std::ptr::null_mut()); + } + } } /// Used by build_memcpy and build_memmove diff --git a/src/context.rs b/src/context.rs index 20802d58241..b0bae551f79 100644 --- a/src/context.rs +++ b/src/context.rs @@ -3,13 +3,13 @@ #[llvm_versions(7.0..=latest)] use crate::InlineAsmDialect; use libc::c_void; -#[llvm_versions(4.0..7.0)] +#[llvm_versions(4.0..=6.0)] use llvm_sys::core::LLVMConstInlineAsm; #[llvm_versions(12.0..=latest)] use llvm_sys::core::LLVMCreateTypeAttribute; #[llvm_versions(7.0..=latest)] use llvm_sys::core::LLVMGetInlineAsm; -#[llvm_versions(4.0..12.0)] +#[llvm_versions(4.0..=11.0)] use llvm_sys::core::LLVMGetTypeByName; #[llvm_versions(12.0..=latest)] use llvm_sys::core::LLVMGetTypeByName2; @@ -19,9 +19,9 @@ use llvm_sys::core::{ LLVMAppendBasicBlockInContext, LLVMConstStringInContext, LLVMConstStructInContext, LLVMContextCreate, LLVMContextDispose, LLVMContextSetDiagnosticHandler, LLVMCreateBuilderInContext, LLVMCreateEnumAttribute, LLVMCreateStringAttribute, LLVMDoubleTypeInContext, LLVMFP128TypeInContext, LLVMFloatTypeInContext, - LLVMGetGlobalContext, LLVMGetMDKindIDInContext, LLVMGetTypeKind, LLVMHalfTypeInContext, - LLVMInsertBasicBlockInContext, LLVMInt16TypeInContext, LLVMInt1TypeInContext, LLVMInt32TypeInContext, - LLVMInt64TypeInContext, LLVMInt8TypeInContext, LLVMIntTypeInContext, LLVMMDNodeInContext, LLVMMDStringInContext, + LLVMGetGlobalContext, LLVMGetMDKindIDInContext, LLVMHalfTypeInContext, LLVMInsertBasicBlockInContext, + LLVMInt16TypeInContext, LLVMInt1TypeInContext, LLVMInt32TypeInContext, LLVMInt64TypeInContext, + LLVMInt8TypeInContext, LLVMIntTypeInContext, LLVMMDNodeInContext, LLVMMDStringInContext, LLVMModuleCreateWithNameInContext, LLVMPPCFP128TypeInContext, LLVMStructCreateNamed, LLVMStructTypeInContext, LLVMVoidTypeInContext, LLVMX86FP80TypeInContext, }; @@ -54,7 +54,6 @@ pub(crate) use private::AsContextRef; use std::marker::PhantomData; use std::mem::forget; - use std::ptr; use std::thread_local; @@ -523,7 +522,6 @@ impl Context { /// ```no_run /// use std::convert::TryFrom; /// use inkwell::context::Context; - /// use inkwell::values::CallableValue; /// /// let context = Context::create(); /// let module = context.create_module("my_module"); @@ -556,8 +554,17 @@ impl Context { /// false, /// ); /// let params = &[context.i64_type().const_int(60, false).into(), context.i64_type().const_int(1, false).into()]; - /// let callable_value = CallableValue::try_from(asm).unwrap(); - /// builder.build_call(callable_value, params, "exit"); + /// + /// #[cfg(not(any(feature = "llvm15-0")))] + /// { + /// use inkwell::values::CallableValue; + /// let callable_value = CallableValue::try_from(asm).unwrap(); + /// builder.build_call(callable_value, params, "exit"); + /// } + /// + /// #[cfg(any(feature = "llvm15-0"))] + /// builder.build_indirect_call(asm_fn, asm, params, "exit"); + /// /// builder.build_return(None); /// ``` #[inline] @@ -1250,6 +1257,7 @@ impl Context { self.context.const_string(string, null_terminated) } + #[allow(dead_code)] #[inline] pub(crate) fn set_diagnostic_handler( &self, @@ -1357,7 +1365,6 @@ impl<'ctx> ContextRef<'ctx> { /// ```no_run /// use std::convert::TryFrom; /// use inkwell::context::Context; - /// use inkwell::values::CallableValue; /// /// let context = Context::create(); /// let module = context.create_module("my_module"); @@ -1390,8 +1397,17 @@ impl<'ctx> ContextRef<'ctx> { /// false, /// ); /// let params = &[context.i64_type().const_int(60, false).into(), context.i64_type().const_int(1, false).into()]; - /// let callable_value = CallableValue::try_from(asm).unwrap(); - /// builder.build_call(callable_value, params, "exit"); + /// + /// #[cfg(not(any(feature = "llvm15-0")))] + /// { + /// use inkwell::values::CallableValue; + /// let callable_value = CallableValue::try_from(asm).unwrap(); + /// builder.build_call(callable_value, params, "exit"); + /// } + /// + /// #[cfg(any(feature = "llvm15-0"))] + /// builder.build_indirect_call(asm_fn, asm, params, "exit"); + /// /// builder.build_return(None); /// ``` #[inline] diff --git a/src/debug_info.rs b/src/debug_info.rs index 9d1a8968ed7..639a4cc186b 100644 --- a/src/debug_info.rs +++ b/src/debug_info.rs @@ -185,14 +185,16 @@ impl<'ctx> DebugInfoBuilder<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sysroot: &str, #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sdk: &str, ) -> (Self, DICompileUnit<'ctx>) { @@ -227,14 +229,16 @@ impl<'ctx> DebugInfoBuilder<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sysroot, #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sdk, ); @@ -272,14 +276,16 @@ impl<'ctx> DebugInfoBuilder<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sysroot: &str, #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sdk: &str, ) -> DICompileUnit<'ctx> { @@ -317,7 +323,8 @@ impl<'ctx> DebugInfoBuilder<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] { LLVMDIBuilderCreateCompileUnit( diff --git a/src/lib.rs b/src/lib.rs index 939e606cf81..c9993db2d3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,8 @@ extern crate llvm_sys_120 as llvm_sys; extern crate llvm_sys_130 as llvm_sys; #[cfg(feature = "llvm14-0")] extern crate llvm_sys_140 as llvm_sys; +#[cfg(feature = "llvm15-0")] +extern crate llvm_sys_150 as llvm_sys; #[cfg(feature = "llvm4-0")] extern crate llvm_sys_40 as llvm_sys; #[cfg(feature = "llvm5-0")] @@ -103,7 +105,7 @@ macro_rules! assert_unique_used_features { } } -assert_unique_used_features! {"llvm4-0", "llvm5-0", "llvm6-0", "llvm7-0", "llvm8-0", "llvm9-0", "llvm10-0", "llvm11-0", "llvm12-0", "llvm13-0", "llvm14-0"} +assert_unique_used_features! {"llvm4-0", "llvm5-0", "llvm6-0", "llvm7-0", "llvm8-0", "llvm9-0", "llvm10-0", "llvm11-0", "llvm12-0", "llvm13-0", "llvm14-0", "llvm15-0"} /// Defines the address space in which a global will be inserted. /// @@ -344,6 +346,16 @@ pub enum AtomicRMWBinOp { #[llvm_versions(10.0..=latest)] #[llvm_variant(LLVMAtomicRMWBinOpFSub)] FSub, + + /// Sets memory to the greater of the two float-typed values, one provided and one from memory. Returns the value that was in memory. + #[llvm_versions(15.0..=latest)] + #[llvm_variant(LLVMAtomicRMWBinOpFMax)] + FMax, + + /// Sets memory to the lesser of the two float-typed values, one provided and one from memory. Returns the value that was in memory. + #[llvm_versions(15.0..=latest)] + #[llvm_variant(LLVMAtomicRMWBinOpFMin)] + FMin, } /// Defines the optimization level used to compile a `Module`. diff --git a/src/module.rs b/src/module.rs index 089b4ed6361..11120cb88b3 100644 --- a/src/module.rs +++ b/src/module.rs @@ -4,7 +4,7 @@ use llvm_sys::analysis::{LLVMVerifierFailureAction, LLVMVerifyModule}; #[allow(deprecated)] use llvm_sys::bit_reader::LLVMParseBitcodeInContext; use llvm_sys::bit_writer::{LLVMWriteBitcodeToFile, LLVMWriteBitcodeToMemoryBuffer}; -#[llvm_versions(4.0..14.0)] +#[llvm_versions(4.0..=14.0)] use llvm_sys::core::LLVMGetTypeByName; use llvm_sys::core::{ @@ -348,7 +348,7 @@ impl<'ctx> Module<'ctx> { /// assert_eq!(module.get_struct_type("foo").unwrap(), opaque); /// ``` /// - #[llvm_versions(4.0..12.0)] + #[llvm_versions(4.0..=11.0)] pub fn get_struct_type(&self, name: &str) -> Option> { let c_string = to_c_str(name); @@ -1097,7 +1097,7 @@ impl<'ctx> Module<'ctx> { unsafe { Some(GlobalValue::new(value)) } } - /// Creates a new `Module` from a `MemoryBuffer`. + /// Creates a new `Module` from a `MemoryBuffer` with bitcode. /// /// # Example /// @@ -1144,7 +1144,7 @@ impl<'ctx> Module<'ctx> { unsafe { Ok(Module::new(module.assume_init())) } } - /// A convenience function for creating a `Module` from a file for a given context. + /// A convenience function for creating a `Module` from a bitcode file for a given context. /// /// # Example /// @@ -1415,14 +1415,16 @@ impl<'ctx> Module<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sysroot: &str, #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sdk: &str, ) -> (DebugInfoBuilder<'ctx>, DICompileUnit<'ctx>) { @@ -1445,14 +1447,16 @@ impl<'ctx> Module<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sysroot, #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] sdk, ) diff --git a/src/passes.rs b/src/passes.rs index 118b1fa1e08..41adb4e39cf 100644 --- a/src/passes.rs +++ b/src/passes.rs @@ -12,17 +12,15 @@ use llvm_sys::prelude::{LLVMPassManagerRef, LLVMPassRegistryRef}; #[llvm_versions(10.0..=latest)] use llvm_sys::transforms::ipo::LLVMAddMergeFunctionsPass; use llvm_sys::transforms::ipo::{ - LLVMAddAlwaysInlinerPass, LLVMAddArgumentPromotionPass, LLVMAddConstantMergePass, LLVMAddDeadArgEliminationPass, - LLVMAddFunctionAttrsPass, LLVMAddFunctionInliningPass, LLVMAddGlobalDCEPass, LLVMAddGlobalOptimizerPass, - LLVMAddIPSCCPPass, LLVMAddInternalizePass, LLVMAddPruneEHPass, LLVMAddStripDeadPrototypesPass, - LLVMAddStripSymbolsPass, + LLVMAddAlwaysInlinerPass, LLVMAddConstantMergePass, LLVMAddDeadArgEliminationPass, LLVMAddFunctionAttrsPass, + LLVMAddFunctionInliningPass, LLVMAddGlobalDCEPass, LLVMAddGlobalOptimizerPass, LLVMAddIPSCCPPass, + LLVMAddInternalizePass, LLVMAddPruneEHPass, LLVMAddStripDeadPrototypesPass, LLVMAddStripSymbolsPass, }; use llvm_sys::transforms::pass_manager_builder::{ LLVMPassManagerBuilderCreate, LLVMPassManagerBuilderDispose, LLVMPassManagerBuilderPopulateFunctionPassManager, - LLVMPassManagerBuilderPopulateLTOPassManager, LLVMPassManagerBuilderPopulateModulePassManager, - LLVMPassManagerBuilderRef, LLVMPassManagerBuilderSetDisableSimplifyLibCalls, - LLVMPassManagerBuilderSetDisableUnitAtATime, LLVMPassManagerBuilderSetDisableUnrollLoops, - LLVMPassManagerBuilderSetOptLevel, LLVMPassManagerBuilderSetSizeLevel, + LLVMPassManagerBuilderPopulateModulePassManager, LLVMPassManagerBuilderRef, + LLVMPassManagerBuilderSetDisableSimplifyLibCalls, LLVMPassManagerBuilderSetDisableUnitAtATime, + LLVMPassManagerBuilderSetDisableUnrollLoops, LLVMPassManagerBuilderSetOptLevel, LLVMPassManagerBuilderSetSizeLevel, LLVMPassManagerBuilderUseInlinerWithThreshold, }; use llvm_sys::transforms::scalar::{ @@ -31,7 +29,7 @@ use llvm_sys::transforms::scalar::{ LLVMAddDeadStoreEliminationPass, LLVMAddDemoteMemoryToRegisterPass, LLVMAddEarlyCSEPass, LLVMAddGVNPass, LLVMAddIndVarSimplifyPass, LLVMAddInstructionCombiningPass, LLVMAddJumpThreadingPass, LLVMAddLICMPass, LLVMAddLoopDeletionPass, LLVMAddLoopIdiomPass, LLVMAddLoopRerollPass, LLVMAddLoopRotatePass, LLVMAddLoopUnrollPass, - LLVMAddLoopUnswitchPass, LLVMAddLowerExpectIntrinsicPass, LLVMAddMemCpyOptPass, LLVMAddMergedLoadStoreMotionPass, + LLVMAddLowerExpectIntrinsicPass, LLVMAddMemCpyOptPass, LLVMAddMergedLoadStoreMotionPass, LLVMAddPartiallyInlineLibCallsPass, LLVMAddReassociatePass, LLVMAddSCCPPass, LLVMAddScalarReplAggregatesPass, LLVMAddScalarReplAggregatesPassSSA, LLVMAddScalarReplAggregatesPassWithThreshold, LLVMAddScalarizerPass, LLVMAddScopedNoAliasAAPass, LLVMAddSimplifyLibCallsPass, LLVMAddTailCallEliminationPass, @@ -183,7 +181,10 @@ impl PassManagerBuilder { /// /// pass_manager_builder.populate_lto_pass_manager(&lpm, false, false); /// ``` + #[llvm_versions(4.0..=14.0)] pub fn populate_lto_pass_manager(&self, pass_manager: &PassManager, internalize: bool, run_inliner: bool) { + use llvm_sys::transforms::pass_manager_builder::LLVMPassManagerBuilderPopulateLTOPassManager; + unsafe { LLVMPassManagerBuilderPopulateLTOPassManager( self.pass_manager_builder, @@ -300,7 +301,10 @@ impl PassManager { /// only stored to (returning the value instead), but does not currently. /// This case would be best handled when and if LLVM starts supporting multiple /// return values from functions. + #[llvm_versions(4.0..=14.0)] pub fn add_argument_promotion_pass(&self) { + use llvm_sys::transforms::ipo::LLVMAddArgumentPromotionPass; + unsafe { LLVMAddArgumentPromotionPass(self.pass_manager) } } @@ -721,7 +725,10 @@ impl PassManager { /// to be run before it to hoist invariant conditions /// out of the loop, to make the unswitching opportunity /// obvious. + #[llvm_versions(4.0..=14.0)] pub fn add_loop_unswitch_pass(&self) { + use llvm_sys::transforms::scalar::LLVMAddLoopUnswitchPass; + unsafe { LLVMAddLoopUnswitchPass(self.pass_manager) } } @@ -757,7 +764,7 @@ impl PassManager { /// order to rewrite loads and stores as appropriate. This is just /// the standard SSA construction algorithm to construct "pruned" SSA form. pub fn add_promote_memory_to_register_pass(&self) { - #[llvm_versions(4.0..7.0)] + #[llvm_versions(4.0..=6.0)] use llvm_sys::transforms::scalar::LLVMAddPromoteMemoryToRegisterPass; #[llvm_versions(7.0..=latest)] use llvm_sys::transforms::util::LLVMAddPromoteMemoryToRegisterPass; @@ -1008,28 +1015,28 @@ impl PassManager { unsafe { LLVMAddLoopUnrollAndJamPass(self.pass_manager) } } - #[llvm_versions(8.0..=latest)] + #[llvm_versions(8.0..15.0)] pub fn add_coroutine_early_pass(&self) { use llvm_sys::transforms::coroutines::LLVMAddCoroEarlyPass; unsafe { LLVMAddCoroEarlyPass(self.pass_manager) } } - #[llvm_versions(8.0..=latest)] + #[llvm_versions(8.0..15.0)] pub fn add_coroutine_split_pass(&self) { use llvm_sys::transforms::coroutines::LLVMAddCoroSplitPass; unsafe { LLVMAddCoroSplitPass(self.pass_manager) } } - #[llvm_versions(8.0..=latest)] + #[llvm_versions(8.0..15.0)] pub fn add_coroutine_elide_pass(&self) { use llvm_sys::transforms::coroutines::LLVMAddCoroElidePass; unsafe { LLVMAddCoroElidePass(self.pass_manager) } } - #[llvm_versions(8.0..=latest)] + #[llvm_versions(8.0..15.0)] pub fn add_coroutine_cleanup_pass(&self) { use llvm_sys::transforms::coroutines::LLVMAddCoroCleanupPass; diff --git a/src/types/array_type.rs b/src/types/array_type.rs index 34b4e9247ad..4309b9aad32 100644 --- a/src/types/array_type.rs +++ b/src/types/array_type.rs @@ -76,6 +76,7 @@ impl<'ctx> ArrayType<'ctx> { /// let i8_array_type = i8_type.array_type(3); /// let i8_array_ptr_type = i8_array_type.ptr_type(AddressSpace::default()); /// + /// #[cfg(not(feature = "llvm15-0"))] /// assert_eq!(i8_array_ptr_type.get_element_type().into_array_type(), i8_array_type); /// ``` pub fn ptr_type(self, address_space: AddressSpace) -> PointerType<'ctx> { diff --git a/src/types/enums.rs b/src/types/enums.rs index 7372c634e15..11189b42d85 100644 --- a/src/types/enums.rs +++ b/src/types/enums.rs @@ -220,7 +220,8 @@ impl<'ctx> AnyTypeEnum<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] LLVMTypeKind::LLVMBFloatTypeKind => AnyTypeEnum::FloatType(FloatType::new(type_)), LLVMTypeKind::LLVMLabelTypeKind => panic!("FIXME: Unsupported type: Label"), @@ -234,13 +235,19 @@ impl<'ctx> AnyTypeEnum<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] LLVMTypeKind::LLVMScalableVectorTypeKind => AnyTypeEnum::VectorType(VectorType::new(type_)), // FIXME: should inkwell support metadata as AnyType? LLVMTypeKind::LLVMMetadataTypeKind => panic!("Metadata type is not supported as AnyType."), LLVMTypeKind::LLVMX86_MMXTypeKind => panic!("FIXME: Unsupported type: MMX"), - #[cfg(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0"))] + #[cfg(any( + feature = "llvm12-0", + feature = "llvm13-0", + feature = "llvm14-0", + feature = "llvm15-0" + ))] LLVMTypeKind::LLVMX86_AMXTypeKind => panic!("FIXME: Unsupported type: AMX"), LLVMTypeKind::LLVMTokenTypeKind => panic!("FIXME: Unsupported type: Token"), } @@ -392,7 +399,8 @@ impl<'ctx> BasicTypeEnum<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] LLVMTypeKind::LLVMBFloatTypeKind => BasicTypeEnum::FloatType(FloatType::new(type_)), LLVMTypeKind::LLVMIntegerTypeKind => BasicTypeEnum::IntType(IntType::new(type_)), @@ -404,19 +412,25 @@ impl<'ctx> BasicTypeEnum<'ctx> { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] LLVMTypeKind::LLVMScalableVectorTypeKind => BasicTypeEnum::VectorType(VectorType::new(type_)), LLVMTypeKind::LLVMMetadataTypeKind => panic!("Unsupported basic type: Metadata"), // see https://llvm.org/docs/LangRef.html#x86-mmx-type LLVMTypeKind::LLVMX86_MMXTypeKind => panic!("Unsupported basic type: MMX"), // see https://llvm.org/docs/LangRef.html#x86-amx-type - #[cfg(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0"))] - LLVMTypeKind::LLVMX86_AMXTypeKind => panic!("Unsupported basic type: AMX"), - LLVMTypeKind::LLVMLabelTypeKind => panic!("Unsupported basic type: Label"), - LLVMTypeKind::LLVMVoidTypeKind => panic!("Unsupported basic type: VoidType"), - LLVMTypeKind::LLVMFunctionTypeKind => panic!("Unsupported basic type: FunctionType"), - LLVMTypeKind::LLVMTokenTypeKind => panic!("Unsupported basic type: Token"), + #[cfg(any( + feature = "llvm12-0", + feature = "llvm13-0", + feature = "llvm14-0", + feature = "llvm15-0" + ))] + LLVMTypeKind::LLVMX86_AMXTypeKind => unreachable!("Unsupported basic type: AMX"), + LLVMTypeKind::LLVMLabelTypeKind => unreachable!("Unsupported basic type: Label"), + LLVMTypeKind::LLVMVoidTypeKind => unreachable!("Unsupported basic type: VoidType"), + LLVMTypeKind::LLVMFunctionTypeKind => unreachable!("Unsupported basic type: FunctionType"), + LLVMTypeKind::LLVMTokenTypeKind => unreachable!("Unsupported basic type: Token"), } } diff --git a/src/types/float_type.rs b/src/types/float_type.rs index 9e3f405fdf2..e7b92006506 100644 --- a/src/types/float_type.rs +++ b/src/types/float_type.rs @@ -215,6 +215,7 @@ impl<'ctx> FloatType<'ctx> { /// let f32_type = context.f32_type(); /// let f32_ptr_type = f32_type.ptr_type(AddressSpace::default()); /// + /// #[cfg(not(feature = "llvm15-0"))] /// assert_eq!(f32_ptr_type.get_element_type().into_float_type(), f32_type); /// ``` pub fn ptr_type(self, address_space: AddressSpace) -> PointerType<'ctx> { diff --git a/src/types/fn_type.rs b/src/types/fn_type.rs index 69ecdd0b269..c831b7bc792 100644 --- a/src/types/fn_type.rs +++ b/src/types/fn_type.rs @@ -45,6 +45,7 @@ impl<'ctx> FunctionType<'ctx> { /// let fn_type = f32_type.fn_type(&[], false); /// let fn_ptr_type = fn_type.ptr_type(AddressSpace::default()); /// + /// #[cfg(not(feature = "llvm15-0"))] /// assert_eq!(fn_ptr_type.get_element_type().into_function_type(), fn_type); /// ``` pub fn ptr_type(self, address_space: AddressSpace) -> PointerType<'ctx> { diff --git a/src/types/int_type.rs b/src/types/int_type.rs index e1d24b59f5b..9580e4f051c 100644 --- a/src/types/int_type.rs +++ b/src/types/int_type.rs @@ -310,6 +310,7 @@ impl<'ctx> IntType<'ctx> { /// let i8_type = context.i8_type(); /// let i8_ptr_type = i8_type.ptr_type(AddressSpace::default()); /// + /// #[cfg(not(feature = "llvm15-0"))] /// assert_eq!(i8_ptr_type.get_element_type().into_int_type(), i8_type); /// ``` pub fn ptr_type(self, address_space: AddressSpace) -> PointerType<'ctx> { diff --git a/src/types/metadata_type.rs b/src/types/metadata_type.rs index c386b3cd558..fb164691d69 100644 --- a/src/types/metadata_type.rs +++ b/src/types/metadata_type.rs @@ -73,7 +73,7 @@ impl AsTypeRef for MetadataType<'_> { self.metadata_type.ty } - #[llvm_versions(4.0..6.0)] + #[llvm_versions(4.0..=5.0)] fn as_type_ref(&self) -> LLVMTypeRef { unimplemented!("MetadataType is only available in LLVM > 6.0") } diff --git a/src/types/ptr_type.rs b/src/types/ptr_type.rs index fedf719e098..a52de1dc416 100644 --- a/src/types/ptr_type.rs +++ b/src/types/ptr_type.rs @@ -4,12 +4,13 @@ use llvm_sys::prelude::{LLVMTypeRef, LLVMValueRef}; use crate::context::ContextRef; use crate::support::LLVMString; use crate::types::traits::AsTypeRef; -use crate::types::{AnyTypeEnum, ArrayType, FunctionType, Type, VectorType}; +#[llvm_versions(4.0..=14.0)] +use crate::types::AnyTypeEnum; +use crate::types::{ArrayType, FunctionType, Type, VectorType}; use crate::values::{ArrayValue, AsValueRef, IntValue, PointerValue}; use crate::AddressSpace; use crate::types::enums::BasicMetadataTypeEnum; -use std::convert::TryFrom; use std::fmt::{self, Display}; /// A `PointerType` is the type of a pointer constant or variable. @@ -78,6 +79,7 @@ impl<'ctx> PointerType<'ctx> { /// let f32_ptr_type = f32_type.ptr_type(AddressSpace::default()); /// let f32_ptr_ptr_type = f32_ptr_type.ptr_type(AddressSpace::default()); /// + /// #[cfg(not(feature = "llvm15-0"))] /// assert_eq!(f32_ptr_ptr_type.get_element_type().into_pointer_type(), f32_ptr_type); /// ``` pub fn ptr_type(self, address_space: AddressSpace) -> PointerType<'ctx> { @@ -258,6 +260,7 @@ impl<'ctx> PointerType<'ctx> { /// /// assert_eq!(f32_ptr_type.get_element_type().into_float_type(), f32_type); /// ``` + #[llvm_versions(4.0..=14.0)] pub fn get_element_type(self) -> AnyTypeEnum<'ctx> { self.ptr_type.get_element_type() } diff --git a/src/types/struct_type.rs b/src/types/struct_type.rs index 3603376a051..bf12a44060b 100644 --- a/src/types/struct_type.rs +++ b/src/types/struct_type.rs @@ -192,6 +192,7 @@ impl<'ctx> StructType<'ctx> { /// let struct_type = context.struct_type(&[f32_type.into(), f32_type.into()], false); /// let struct_ptr_type = struct_type.ptr_type(AddressSpace::default()); /// + /// #[cfg(not(feature = "llvm15-0"))] /// assert_eq!(struct_ptr_type.get_element_type().into_struct_type(), struct_type); /// ``` pub fn ptr_type(self, address_space: AddressSpace) -> PointerType<'ctx> { diff --git a/src/types/vec_type.rs b/src/types/vec_type.rs index 5e042d5b551..641833b0a45 100644 --- a/src/types/vec_type.rs +++ b/src/types/vec_type.rs @@ -181,6 +181,7 @@ impl<'ctx> VectorType<'ctx> { /// let f32_vec_type = f32_type.vec_type(3); /// let f32_vec_ptr_type = f32_vec_type.ptr_type(AddressSpace::default()); /// + /// #[cfg(not(feature = "llvm15-0"))] /// assert_eq!(f32_vec_ptr_type.get_element_type().into_vector_type(), f32_vec_type); /// ``` pub fn ptr_type(self, address_space: AddressSpace) -> PointerType<'ctx> { diff --git a/src/values/callable_value.rs b/src/values/callable_value.rs index 02a2d094466..cc1fbb73c2a 100644 --- a/src/values/callable_value.rs +++ b/src/values/callable_value.rs @@ -6,7 +6,7 @@ use crate::types::AsTypeRef; use crate::values::AsValueRef; use crate::values::{AnyValue, FunctionValue, PointerValue}; -use llvm_sys::core::{LLVMGetElementType, LLVMGetReturnType, LLVMGetTypeKind, LLVMTypeOf}; +use llvm_sys::core::{LLVMGetElementType, LLVMGetTypeKind, LLVMTypeOf}; use llvm_sys::prelude::LLVMTypeRef; use llvm_sys::prelude::LLVMValueRef; use llvm_sys::LLVMTypeKind; @@ -104,7 +104,10 @@ impl<'ctx> AsTypeRef for CallableValue<'ctx> { } impl<'ctx> CallableValue<'ctx> { + #[llvm_versions(4.0..=14.0)] pub(crate) fn returns_void(&self) -> bool { + use llvm_sys::core::LLVMGetReturnType; + let return_type = unsafe { LLVMGetTypeKind(LLVMGetReturnType(LLVMGetElementType(LLVMTypeOf(self.as_value_ref())))) }; diff --git a/src/values/float_value.rs b/src/values/float_value.rs index c3ba6c85dc1..7a87e691965 100644 --- a/src/values/float_value.rs +++ b/src/values/float_value.rs @@ -1,6 +1,6 @@ use llvm_sys::core::{ - LLVMConstFAdd, LLVMConstFCmp, LLVMConstFDiv, LLVMConstFMul, LLVMConstFNeg, LLVMConstFPCast, LLVMConstFPExt, - LLVMConstFPToSI, LLVMConstFPToUI, LLVMConstFPTrunc, LLVMConstFRem, LLVMConstFSub, LLVMConstRealGetDouble, + LLVMConstFCmp, LLVMConstFNeg, LLVMConstFPCast, LLVMConstFPExt, LLVMConstFPToSI, LLVMConstFPToUI, LLVMConstFPTrunc, + LLVMConstRealGetDouble, }; use llvm_sys::prelude::LLVMValueRef; @@ -64,23 +64,38 @@ impl<'ctx> FloatValue<'ctx> { unsafe { FloatValue::new(LLVMConstFNeg(self.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_add(self, rhs: FloatValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstFAdd; + unsafe { FloatValue::new(LLVMConstFAdd(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_sub(self, rhs: FloatValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstFSub; + unsafe { FloatValue::new(LLVMConstFSub(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_mul(self, rhs: FloatValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstFMul; + unsafe { FloatValue::new(LLVMConstFMul(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_div(self, rhs: FloatValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstFDiv; + unsafe { FloatValue::new(LLVMConstFDiv(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_remainder(self, rhs: FloatValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstFRem; + unsafe { FloatValue::new(LLVMConstFRem(self.as_value_ref(), rhs.as_value_ref())) } } diff --git a/src/values/fn_value.rs b/src/values/fn_value.rs index c20b1ec9a31..ad1261927e7 100644 --- a/src/values/fn_value.rs +++ b/src/values/fn_value.rs @@ -25,7 +25,7 @@ use crate::basic_block::BasicBlock; use crate::debug_info::DISubprogram; use crate::module::Linkage; use crate::support::to_c_str; -use crate::types::{FunctionType, PointerType}; +use crate::types::FunctionType; use crate::values::traits::{AnyValue, AsValueRef}; use crate::values::{BasicValueEnum, GlobalValue, Value}; @@ -198,12 +198,20 @@ impl<'ctx> FunctionValue<'ctx> { LLVMDeleteFunction(self.as_value_ref()) } + #[llvm_versions(4.0..=7.0)] pub fn get_type(self) -> FunctionType<'ctx> { + use crate::types::PointerType; + let ptr_type = unsafe { PointerType::new(self.fn_value.get_type()) }; ptr_type.get_element_type().into_function_type() } + #[llvm_versions(8.0..=latest)] + pub fn get_type(self) -> FunctionType<'ctx> { + unsafe { FunctionType::new(llvm_sys::core::LLVMGlobalGetValueType(self.as_value_ref())) } + } + // TODOC: How this works as an exception handler pub fn has_personality_function(self) -> bool { use llvm_sys::core::LLVMHasPersonalityFn; diff --git a/src/values/global_value.rs b/src/values/global_value.rs index aa5afb4db17..38dd68f3613 100644 --- a/src/values/global_value.rs +++ b/src/values/global_value.rs @@ -1,6 +1,6 @@ #[llvm_versions(8.0..=latest)] use llvm_sys::core::LLVMGlobalSetMetadata; -#[llvm_versions(4.0..8.0)] +#[llvm_versions(4.0..=7.0)] use llvm_sys::core::{ LLVMDeleteGlobal, LLVMGetAlignment, LLVMGetDLLStorageClass, LLVMGetInitializer, LLVMGetLinkage, LLVMGetNextGlobal, LLVMGetPreviousGlobal, LLVMGetSection, LLVMGetThreadLocalMode, LLVMGetVisibility, LLVMIsDeclaration, @@ -161,7 +161,7 @@ impl<'ctx> GlobalValue<'ctx> { unsafe { LLVMIsDeclaration(self.as_value_ref()) == 1 } } - #[llvm_versions(4.0..7.0)] + #[llvm_versions(4.0..=6.0)] pub fn has_unnamed_addr(self) -> bool { unsafe { LLVMHasUnnamedAddr(self.as_value_ref()) == 1 } } @@ -171,7 +171,7 @@ impl<'ctx> GlobalValue<'ctx> { unsafe { LLVMGetUnnamedAddress(self.as_value_ref()) == LLVMUnnamedAddr::LLVMGlobalUnnamedAddr } } - #[llvm_versions(4.0..7.0)] + #[llvm_versions(4.0..=6.0)] pub fn set_unnamed_addr(self, has_unnamed_addr: bool) { unsafe { LLVMSetUnnamedAddr(self.as_value_ref(), has_unnamed_addr as i32) } } diff --git a/src/values/int_value.rs b/src/values/int_value.rs index 5e4846569a0..db00cc858b5 100644 --- a/src/values/int_value.rs +++ b/src/values/int_value.rs @@ -1,13 +1,10 @@ -#[llvm_versions(4.0..=latest)] -use llvm_sys::core::LLVMConstExactUDiv; use llvm_sys::core::{ - LLVMConstAShr, LLVMConstAdd, LLVMConstAnd, LLVMConstBitCast, LLVMConstExactSDiv, LLVMConstICmp, LLVMConstIntCast, + LLVMConstAShr, LLVMConstAdd, LLVMConstAnd, LLVMConstBitCast, LLVMConstICmp, LLVMConstIntCast, LLVMConstIntGetSExtValue, LLVMConstIntGetZExtValue, LLVMConstIntToPtr, LLVMConstLShr, LLVMConstMul, LLVMConstNSWAdd, LLVMConstNSWMul, LLVMConstNSWNeg, LLVMConstNSWSub, LLVMConstNUWAdd, LLVMConstNUWMul, - LLVMConstNUWNeg, LLVMConstNUWSub, LLVMConstNeg, LLVMConstNot, LLVMConstOr, LLVMConstSDiv, LLVMConstSExt, - LLVMConstSExtOrBitCast, LLVMConstSIToFP, LLVMConstSRem, LLVMConstSelect, LLVMConstShl, LLVMConstSub, - LLVMConstTrunc, LLVMConstTruncOrBitCast, LLVMConstUDiv, LLVMConstUIToFP, LLVMConstURem, LLVMConstXor, - LLVMConstZExt, LLVMConstZExtOrBitCast, LLVMIsAConstantInt, + LLVMConstNUWNeg, LLVMConstNUWSub, LLVMConstNeg, LLVMConstNot, LLVMConstOr, LLVMConstSExt, LLVMConstSExtOrBitCast, + LLVMConstSIToFP, LLVMConstSelect, LLVMConstShl, LLVMConstSub, LLVMConstTrunc, LLVMConstTruncOrBitCast, + LLVMConstUIToFP, LLVMConstXor, LLVMConstZExt, LLVMConstZExtOrBitCast, LLVMIsAConstantInt, }; use llvm_sys::prelude::LLVMValueRef; @@ -120,28 +117,45 @@ impl<'ctx> IntValue<'ctx> { unsafe { IntValue::new(LLVMConstNUWMul(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_unsigned_div(self, rhs: IntValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstUDiv; + unsafe { IntValue::new(LLVMConstUDiv(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_signed_div(self, rhs: IntValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstSDiv; + unsafe { IntValue::new(LLVMConstSDiv(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_exact_signed_div(self, rhs: IntValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstExactSDiv; + unsafe { IntValue::new(LLVMConstExactSDiv(self.as_value_ref(), rhs.as_value_ref())) } } - #[llvm_versions(4.0..=latest)] + #[llvm_versions(4.0..=14.0)] pub fn const_exact_unsigned_div(self, rhs: IntValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstExactUDiv; + unsafe { IntValue::new(LLVMConstExactUDiv(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_unsigned_remainder(self, rhs: IntValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstURem; + unsafe { IntValue::new(LLVMConstURem(self.as_value_ref(), rhs.as_value_ref())) } } + #[llvm_versions(4.0..=14.0)] pub fn const_signed_remainder(self, rhs: IntValue<'ctx>) -> Self { + use llvm_sys::core::LLVMConstSRem; + unsafe { IntValue::new(LLVMConstSRem(self.as_value_ref(), rhs.as_value_ref())) } } diff --git a/src/values/metadata_value.rs b/src/values/metadata_value.rs index cdb8734fed6..3a796d1c6aa 100644 --- a/src/values/metadata_value.rs +++ b/src/values/metadata_value.rs @@ -32,8 +32,10 @@ pub const FIRST_CUSTOM_METADATA_KIND_ID: u32 = 26; pub const FIRST_CUSTOM_METADATA_KIND_ID: u32 = 28; #[cfg(any(feature = "llvm10-0", feature = "llvm11-0"))] pub const FIRST_CUSTOM_METADATA_KIND_ID: u32 = 30; -#[cfg(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0"))] +#[cfg(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0",))] pub const FIRST_CUSTOM_METADATA_KIND_ID: u32 = 31; +#[cfg(any(feature = "llvm15-0"))] +pub const FIRST_CUSTOM_METADATA_KIND_ID: u32 = 36; #[derive(PartialEq, Eq, Clone, Copy, Hash)] pub struct MetadataValue<'ctx> { diff --git a/src/values/mod.rs b/src/values/mod.rs index 38d1131eeb4..e3a613be9dc 100644 --- a/src/values/mod.rs +++ b/src/values/mod.rs @@ -6,7 +6,6 @@ mod array_value; mod basic_value_use; #[deny(missing_docs)] mod call_site_value; -mod callable_value; mod enums; mod float_value; mod fn_value; @@ -21,11 +20,16 @@ mod struct_value; mod traits; mod vec_value; +#[cfg(not(any(feature = "llvm15-0")))] +mod callable_value; + +#[cfg(not(any(feature = "llvm15-0")))] +pub use crate::values::callable_value::CallableValue; + use crate::support::{to_c_str, LLVMString}; pub use crate::values::array_value::ArrayValue; pub use crate::values::basic_value_use::BasicValueUse; pub use crate::values::call_site_value::CallSiteValue; -pub use crate::values::callable_value::CallableValue; pub use crate::values::enums::{AggregateValueEnum, AnyValueEnum, BasicMetadataValueEnum, BasicValueEnum}; pub use crate::values::float_value::FloatValue; pub use crate::values::fn_value::FunctionValue; @@ -101,13 +105,12 @@ impl<'ctx> Value<'ctx> { // add a ParamValue wrapper type that always have it but conditional types (IntValue) // that also have it. This isn't a huge deal though, since it hasn't proven to be UB so far fn set_name(self, name: &str) { + let c_string = to_c_str(name); + #[cfg(any(feature = "llvm4-0", feature = "llvm5-0", feature = "llvm6-0"))] { - use crate::support::to_c_str; use llvm_sys::core::LLVMSetValueName; - let c_string = to_c_str(name); - unsafe { LLVMSetValueName(self.value, c_string.as_ptr()); } @@ -116,7 +119,7 @@ impl<'ctx> Value<'ctx> { { use llvm_sys::core::LLVMSetValueName2; - unsafe { LLVMSetValueName2(self.value, name.as_ptr() as *const ::libc::c_char, name.len()) } + unsafe { LLVMSetValueName2(self.value, c_string.as_ptr(), name.len()) } } } diff --git a/src/values/ptr_value.rs b/src/values/ptr_value.rs index cbaf959f213..c50f7ba313e 100644 --- a/src/values/ptr_value.rs +++ b/src/values/ptr_value.rs @@ -1,6 +1,6 @@ -#[llvm_versions(4.0..14.0)] +#[llvm_versions(4.0..=14.0)] use llvm_sys::core::{LLVMConstGEP, LLVMConstInBoundsGEP}; -#[llvm_versions(14.0..=latest)] +#[llvm_versions(15.0..=latest)] use llvm_sys::core::{LLVMConstGEP2, LLVMConstInBoundsGEP2}; use llvm_sys::core::{LLVMConstAddrSpaceCast, LLVMConstPointerCast, LLVMConstPtrToInt}; @@ -10,7 +10,7 @@ use std::convert::TryFrom; use std::ffi::CStr; use std::fmt::{self, Display}; -use crate::types::{AsTypeRef, IntType, PointerType}; +use crate::types::{AsTypeRef, BasicType, IntType, PointerType}; use crate::values::{AsValueRef, InstructionValue, IntValue, Value}; use super::AnyValue; @@ -77,46 +77,30 @@ impl<'ctx> PointerValue<'ctx> { // REVIEW: Should this be on array value too? /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(4.0..=14.0)] pub unsafe fn const_gep(self, ordered_indexes: &[IntValue<'ctx>]) -> PointerValue<'ctx> { let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] let value = { - LLVMConstGEP2( - self.get_type().get_element_type().as_type_ref(), + LLVMConstGEP( self.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, ) }; - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] + PointerValue::new(value) + } + + // REVIEW: Should this be on array value too? + /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(15.0..=latest)] + pub unsafe fn const_gep>(self, ty: T, ordered_indexes: &[IntValue<'ctx>]) -> PointerValue<'ctx> { + let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); + let value = { - LLVMConstGEP( + LLVMConstGEP2( + ty.as_type_ref(), self.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, @@ -127,46 +111,33 @@ impl<'ctx> PointerValue<'ctx> { } /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(4.0..=14.0)] pub unsafe fn const_in_bounds_gep(self, ordered_indexes: &[IntValue<'ctx>]) -> PointerValue<'ctx> { let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); - // This ugly cfg specification is due to limitation of custom attributes (for more information, see https://github.com/rust-lang/rust/issues/54727). - // Once custom attriutes inside methods are enabled, this should be replaced with #[llvm_version(14.0..=latest)] - #[cfg(not(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - )))] let value = { - LLVMConstInBoundsGEP2( - self.get_type().get_element_type().as_type_ref(), + LLVMConstInBoundsGEP( self.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, ) }; - #[cfg(any( - feature = "llvm4-0", - feature = "llvm5-0", - feature = "llvm6-0", - feature = "llvm7-0", - feature = "llvm8-0", - feature = "llvm9-0", - feature = "llvm10-0", - feature = "llvm11-0", - feature = "llvm12-0", - feature = "llvm13-0", - ))] + PointerValue::new(value) + } + + /// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future. + #[llvm_versions(15.0..=latest)] + pub unsafe fn const_in_bounds_gep>( + self, + ty: T, + ordered_indexes: &[IntValue<'ctx>], + ) -> PointerValue<'ctx> { + let mut index_values: Vec = ordered_indexes.iter().map(|val| val.as_value_ref()).collect(); + let value = { - LLVMConstInBoundsGEP( + LLVMConstInBoundsGEP2( + ty.as_type_ref(), self.as_value_ref(), index_values.as_mut_ptr(), index_values.len() as u32, diff --git a/src/values/traits.rs b/src/values/traits.rs index 366f74e9f18..4fb6c51e0d6 100644 --- a/src/values/traits.rs +++ b/src/values/traits.rs @@ -1,4 +1,3 @@ -use llvm_sys::core::{LLVMConstExtractValue, LLVMConstInsertValue}; use llvm_sys::prelude::LLVMValueRef; use std::fmt::Debug; @@ -55,7 +54,10 @@ pub trait AggregateValue<'ctx>: BasicValue<'ctx> { // REVIEW: How does LLVM treat out of bound index? Maybe we should return an Option? // or is that only in bounds GEP // REVIEW: Should this be AggregatePointerValue? + #[llvm_versions(4.0..=14.0)] fn const_extract_value(&self, indexes: &mut [u32]) -> BasicValueEnum<'ctx> { + use llvm_sys::core::LLVMConstExtractValue; + unsafe { BasicValueEnum::new(LLVMConstExtractValue( self.as_value_ref(), @@ -66,7 +68,10 @@ pub trait AggregateValue<'ctx>: BasicValue<'ctx> { } // SubTypes: value should really be T in self: VectorValue I think + #[llvm_versions(4.0..=14.0)] fn const_insert_value>(&self, value: BV, indexes: &mut [u32]) -> BasicValueEnum<'ctx> { + use llvm_sys::core::LLVMConstInsertValue; + unsafe { BasicValueEnum::new(LLVMConstInsertValue( self.as_value_ref(), diff --git a/tests/all/test_builder.rs b/tests/all/test_builder.rs index 20a74b9bd9c..c0426150a46 100644 --- a/tests/all/test_builder.rs +++ b/tests/all/test_builder.rs @@ -1,6 +1,4 @@ use inkwell::context::Context; -use inkwell::values::BasicValue; -use inkwell::values::CallableValue; use inkwell::{AddressSpace, AtomicOrdering, AtomicRMWBinOp, OptimizationLevel}; use std::convert::TryFrom; @@ -57,10 +55,19 @@ fn test_build_call() { builder.build_store(alloca, fn_ptr); + #[cfg(not(feature = "llvm15-0"))] let load = builder.build_load(alloca, "load").into_pointer_value(); + #[cfg(feature = "llvm15-0")] + let load = builder.build_load(fn_ptr_type, alloca, "load").into_pointer_value(); - let callable_value = CallableValue::try_from(load).unwrap(); - builder.build_call(callable_value, &[], "call"); + #[cfg(not(feature = "llvm15-0"))] + { + use inkwell::values::CallableValue; + let callable_value = CallableValue::try_from(load).unwrap(); + builder.build_call(callable_value, &[], "call"); + } + #[cfg(feature = "llvm15-0")] + builder.build_indirect_call(fn_type2, load, &[], "call"); builder.build_return(None); assert!(module.verify().is_ok()); @@ -331,7 +338,10 @@ fn test_null_checked_ptr_ops() { let ptr_as_int = builder.build_ptr_to_int(ptr, i64_type, "ptr_as_int"); let new_ptr_as_int = builder.build_int_add(ptr_as_int, one, "add"); let new_ptr = builder.build_int_to_ptr(new_ptr_as_int, i8_ptr_type, "int_as_ptr"); + #[cfg(not(feature = "llvm15-0"))] let index1 = builder.build_load(new_ptr, "deref"); + #[cfg(feature = "llvm15-0")] + let index1 = builder.build_load(i8_ptr_type, new_ptr, "deref"); builder.build_return(Some(&index1)); @@ -368,7 +378,10 @@ fn test_null_checked_ptr_ops() { let ptr_as_int = builder.build_ptr_to_int(ptr, i64_type, "ptr_as_int"); let new_ptr_as_int = builder.build_int_add(ptr_as_int, one, "add"); let new_ptr = builder.build_int_to_ptr(new_ptr_as_int, i8_ptr_type, "int_as_ptr"); + #[cfg(not(feature = "llvm15-0"))] let index1 = builder.build_load(new_ptr, "deref"); + #[cfg(feature = "llvm15-0")] + let index1 = builder.build_load(i8_ptr_type, new_ptr, "deref"); builder.build_return(Some(&index1)); @@ -876,7 +889,12 @@ fn test_insert_value() { builder.position_at_end(entry); let array_alloca = builder.build_alloca(array_type, "array_alloca"); + #[cfg(not(feature = "llvm15-0"))] let array = builder.build_load(array_alloca, "array_load").into_array_value(); + #[cfg(feature = "llvm15-0")] + let array = builder + .build_load(array_type, array_alloca, "array_load") + .into_array_value(); let const_int1 = i32_type.const_int(2, false); let const_int2 = i32_type.const_int(5, false); let const_int3 = i32_type.const_int(6, false); @@ -903,7 +921,12 @@ fn test_insert_value() { assert!(builder.build_extract_value(array, 3, "extract").is_none()); let struct_alloca = builder.build_alloca(struct_type, "struct_alloca"); + #[cfg(not(feature = "llvm15-0"))] let struct_value = builder.build_load(struct_alloca, "struct_load").into_struct_value(); + #[cfg(feature = "llvm15-0")] + let struct_value = builder + .build_load(struct_type, struct_alloca, "struct_load") + .into_struct_value(); assert!(builder .build_insert_value(struct_value, const_int2, 0, "insert") @@ -998,12 +1021,17 @@ fn run_memcpy_on<'ctx>( builder.position_at_end(entry); let len_value = i64_type.const_int(array_len as u64, false); + let element_type = i32_type; + let array_type = element_type.array_type(array_len as u32); let array_ptr = builder.build_array_malloc(i32_type, len_value, "array_ptr").unwrap(); // Initialize the array with the values [1, 2, 3, 4] for index in 0..4 { let index_val = i32_type.const_int(index, false); + #[cfg(not(feature = "llvm15-0"))] let elem_ptr = unsafe { builder.build_in_bounds_gep(array_ptr, &[index_val], "index") }; + #[cfg(feature = "llvm15-0")] + let elem_ptr = unsafe { builder.build_in_bounds_gep(element_type, array_ptr, &[index_val], "index") }; let int_val = i32_type.const_int(index + 1, false); builder.build_store(elem_ptr, int_val); @@ -1014,7 +1042,10 @@ fn run_memcpy_on<'ctx>( let bytes_to_copy = elems_to_copy * std::mem::size_of::(); let size_val = i64_type.const_int(bytes_to_copy as u64, false); let index_val = i32_type.const_int(2, false); + #[cfg(not(feature = "llvm15-0"))] let dest_ptr = unsafe { builder.build_in_bounds_gep(array_ptr, &[index_val], "index") }; + #[cfg(feature = "llvm15-0")] + let dest_ptr = unsafe { builder.build_in_bounds_gep(element_type, array_ptr, &[index_val], "index") }; builder.build_memcpy(dest_ptr, alignment, array_ptr, alignment, size_val)?; @@ -1045,7 +1076,7 @@ fn test_memcpy() { let func = execution_engine .get_function:: *const i32>("test_fn") .unwrap(); - let actual = std::slice::from_raw_parts(func.call(), 4); + let actual: &[i32] = std::slice::from_raw_parts(func.call(), 4); assert_eq!(&[1, 2, 1, 2], actual); } @@ -1068,12 +1099,17 @@ fn run_memmove_on<'ctx>( builder.position_at_end(entry); let len_value = i64_type.const_int(array_len as u64, false); + let element_type = i32_type; + let array_type = element_type.array_type(array_len as u32); let array_ptr = builder.build_array_malloc(i32_type, len_value, "array_ptr").unwrap(); // Initialize the array with the values [1, 2, 3, 4] for index in 0..4 { let index_val = i32_type.const_int(index, false); + #[cfg(not(feature = "llvm15-0"))] let elem_ptr = unsafe { builder.build_in_bounds_gep(array_ptr, &[index_val], "index") }; + #[cfg(feature = "llvm15-0")] + let elem_ptr = unsafe { builder.build_in_bounds_gep(element_type, array_ptr, &[index_val], "index") }; let int_val = i32_type.const_int(index + 1, false); builder.build_store(elem_ptr, int_val); @@ -1084,7 +1120,10 @@ fn run_memmove_on<'ctx>( let bytes_to_copy = elems_to_copy * std::mem::size_of::(); let size_val = i64_type.const_int(bytes_to_copy as u64, false); let index_val = i32_type.const_int(2, false); + #[cfg(not(feature = "llvm15-0"))] let dest_ptr = unsafe { builder.build_in_bounds_gep(array_ptr, &[index_val], "index") }; + #[cfg(feature = "llvm15-0")] + let dest_ptr = unsafe { builder.build_in_bounds_gep(element_type, array_ptr, &[index_val], "index") }; builder.build_memmove(dest_ptr, alignment, array_ptr, alignment, size_val)?; @@ -1139,6 +1178,8 @@ fn run_memset_on<'ctx>( builder.position_at_end(entry); let len_value = i64_type.const_int(array_len as u64, false); + let element_type = i32_type; + let array_type = element_type.array_type(array_len as u32); let array_ptr = builder.build_array_malloc(i32_type, len_value, "array_ptr").unwrap(); let elems_to_copy = 2; @@ -1150,7 +1191,10 @@ fn run_memset_on<'ctx>( // Memset the second half of the array as -1 let val = i8_type.const_all_ones(); let index = i32_type.const_int(2, false); + #[cfg(not(feature = "llvm15-0"))] let part_2 = unsafe { builder.build_in_bounds_gep(array_ptr, &[index], "index") }; + #[cfg(feature = "llvm15-0")] + let part_2 = unsafe { builder.build_in_bounds_gep(element_type, array_ptr, &[index], "index") }; builder.build_memset(part_2, alignment, val, size_val)?; builder.build_return(Some(&array_ptr)); @@ -1187,6 +1231,8 @@ fn test_memset() { #[test] fn test_bitcast() { + use inkwell::values::BasicValue; + let context = Context::create(); let module = context.create_module("bc"); let void_type = context.void_type(); @@ -1256,10 +1302,13 @@ fn test_atomicrmw() { let result = builder.build_atomicrmw(AtomicRMWBinOp::Add, ptr_value, zero_value, AtomicOrdering::Unordered); assert!(result.is_ok()); - let ptr_value = i64_type.ptr_type(AddressSpace::default()).get_undef(); - let zero_value = i32_type.const_zero(); - let result = builder.build_atomicrmw(AtomicRMWBinOp::Add, ptr_value, zero_value, AtomicOrdering::Unordered); - assert!(result.is_err()); + #[cfg(not(any(feature = "llvm15-0")))] + { + let ptr_value = i64_type.ptr_type(AddressSpace::default()).get_undef(); + let zero_value = i32_type.const_zero(); + let result = builder.build_atomicrmw(AtomicRMWBinOp::Add, ptr_value, zero_value, AtomicOrdering::Unordered); + assert!(result.is_err()); + } let ptr_value = i31_type.ptr_type(AddressSpace::default()).get_undef(); let zero_value = i31_type.const_zero(); @@ -1361,17 +1410,20 @@ fn test_cmpxchg() { ); assert!(result.is_err()); - let ptr_value = i32_ptr_ptr_type.get_undef(); - let zero_value = i32_type.const_zero(); - let neg_one_value = i32_type.const_all_ones(); - let result = builder.build_cmpxchg( - ptr_value, - zero_value, - neg_one_value, - AtomicOrdering::Monotonic, - AtomicOrdering::Monotonic, - ); - assert!(result.is_err()); + #[cfg(not(any(feature = "llvm15-0")))] + { + let ptr_value = i32_ptr_ptr_type.get_undef(); + let zero_value = i32_type.const_zero(); + let neg_one_value = i32_type.const_all_ones(); + let result = builder.build_cmpxchg( + ptr_value, + zero_value, + neg_one_value, + AtomicOrdering::Monotonic, + AtomicOrdering::Monotonic, + ); + assert!(result.is_err()); + } let ptr_value = i32_ptr_type.get_undef(); let zero_value = i64_type.const_zero(); @@ -1409,17 +1461,20 @@ fn test_cmpxchg() { ); assert!(result.is_ok()); - let ptr_value = i32_ptr_type.get_undef(); - let zero_value = i32_ptr_type.const_zero(); - let neg_one_value = i32_ptr_type.const_zero(); - let result = builder.build_cmpxchg( - ptr_value, - zero_value, - neg_one_value, - AtomicOrdering::Monotonic, - AtomicOrdering::Monotonic, - ); - assert!(result.is_err()); + #[cfg(not(any(feature = "llvm15-0")))] + { + let ptr_value = i32_ptr_type.get_undef(); + let zero_value = i32_ptr_type.const_zero(); + let neg_one_value = i32_ptr_type.const_zero(); + let result = builder.build_cmpxchg( + ptr_value, + zero_value, + neg_one_value, + AtomicOrdering::Monotonic, + AtomicOrdering::Monotonic, + ); + assert!(result.is_err()); + } } #[test] @@ -1442,9 +1497,22 @@ fn test_safe_struct_gep() { let i32_ptr = fn_value.get_first_param().unwrap().into_pointer_value(); let struct_ptr = fn_value.get_last_param().unwrap().into_pointer_value(); - assert!(builder.build_struct_gep(i32_ptr, 0, "struct_gep").is_err()); - assert!(builder.build_struct_gep(i32_ptr, 10, "struct_gep").is_err()); - assert!(builder.build_struct_gep(struct_ptr, 0, "struct_gep").is_ok()); - assert!(builder.build_struct_gep(struct_ptr, 1, "struct_gep").is_ok()); - assert!(builder.build_struct_gep(struct_ptr, 2, "struct_gep").is_err()); + #[cfg(not(feature = "llvm15-0"))] + { + assert!(builder.build_struct_gep(i32_ptr, 0, "struct_gep").is_err()); + assert!(builder.build_struct_gep(i32_ptr, 10, "struct_gep").is_err()); + assert!(builder.build_struct_gep(struct_ptr, 0, "struct_gep").is_ok()); + assert!(builder.build_struct_gep(struct_ptr, 1, "struct_gep").is_ok()); + assert!(builder.build_struct_gep(struct_ptr, 2, "struct_gep").is_err()); + } + #[cfg(feature = "llvm15-0")] + { + assert!(builder.build_struct_gep(i32_ty, i32_ptr, 0, "struct_gep").is_err()); + assert!(builder.build_struct_gep(i32_ty, i32_ptr, 10, "struct_gep").is_err()); + assert!(builder.build_struct_gep(struct_ty, struct_ptr, 0, "struct_gep").is_ok()); + assert!(builder.build_struct_gep(struct_ty, struct_ptr, 1, "struct_gep").is_ok()); + assert!(builder + .build_struct_gep(struct_ty, struct_ptr, 2, "struct_gep") + .is_err()); + } } diff --git a/tests/all/test_debug_info.rs b/tests/all/test_debug_info.rs index 415f69dccf9..20cb3c27ce8 100644 --- a/tests/all/test_debug_info.rs +++ b/tests/all/test_debug_info.rs @@ -29,14 +29,16 @@ fn test_smoke() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -77,7 +79,11 @@ fn test_smoke() { let lexical_block = dibuilder.create_lexical_block(func_scope.as_debug_info_scope(), compile_unit.get_file(), 0, 0); let loc = dibuilder.create_debug_location(&context, 0, 0, lexical_block.as_debug_info_scope(), None); + + #[cfg(any(feature = "llvm7-0", feature = "llvm8-0",))] builder.set_current_debug_location(&context, loc); + #[cfg(not(any(feature = "llvm7-0", feature = "llvm8-0",)))] + builder.set_current_debug_location(loc); dibuilder.finalize(); @@ -107,14 +113,16 @@ fn test_struct_with_placeholders() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -231,14 +239,16 @@ fn test_no_explicit_finalize() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -272,14 +282,16 @@ fn test_replacing_placeholder_with_placeholder() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -327,14 +339,16 @@ fn test_anonymous_basic_type() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -375,14 +389,16 @@ fn test_global_expressions() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -441,14 +457,16 @@ fn test_pointer_types() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -491,14 +509,16 @@ fn test_reference_types() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); @@ -541,14 +561,16 @@ fn test_array_type() { feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", #[cfg(any( feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0", - feature = "llvm14-0" + feature = "llvm14-0", + feature = "llvm15-0" ))] "", ); diff --git a/tests/all/test_instruction_values.rs b/tests/all/test_instruction_values.rs index 739e1b53e5f..539b685a866 100644 --- a/tests/all/test_instruction_values.rs +++ b/tests/all/test_instruction_values.rs @@ -4,6 +4,7 @@ use inkwell::values::{BasicValue, InstructionOpcode::*}; use inkwell::{AddressSpace, AtomicOrdering, AtomicRMWBinOp, FloatPredicate, IntPredicate}; #[test] +#[ignore] fn test_operands() { let context = Context::create(); let module = context.create_module("ivs"); @@ -45,23 +46,25 @@ fn test_operands() { let free_operand0 = free_instruction.get_operand(0).unwrap().left().unwrap(); let free_operand1 = free_instruction.get_operand(1).unwrap().left().unwrap(); - let free_operand0_instruction = free_operand0.as_instruction_value().unwrap(); assert!(free_operand0.is_pointer_value()); // (implictly casted) i8* arg1 assert!(free_operand1.is_pointer_value()); // Free function ptr + assert!(free_instruction.get_operand(2).is_none()); + assert!(free_instruction.get_operand(3).is_none()); + assert!(free_instruction.get_operand(4).is_none()); + + let free_operand0_instruction = free_operand0.as_instruction_value().unwrap(); assert_eq!(free_operand0_instruction.get_opcode(), BitCast); assert_eq!(free_operand0_instruction.get_operand(0).unwrap().left().unwrap(), arg1); assert!(free_operand0_instruction.get_operand(1).is_none()); assert!(free_operand0_instruction.get_operand(2).is_none()); - assert!(free_instruction.get_operand(2).is_none()); - assert!(free_instruction.get_operand(3).is_none()); - assert!(free_instruction.get_operand(4).is_none()); assert!(module.verify().is_ok()); assert!(free_instruction.set_operand(0, arg1)); // Module is no longer valid because free takes an i8* not f32* + #[cfg(not(feature = "llvm15-0"))] assert!(module.verify().is_err()); assert!(free_instruction.set_operand(0, free_operand0)); @@ -417,7 +420,10 @@ fn test_mem_instructions() { let f32_val = f32_type.const_float(::std::f64::consts::PI); let store_instruction = builder.build_store(arg1, f32_val); + #[cfg(not(feature = "llvm15-0"))] let load = builder.build_load(arg1, ""); + #[cfg(feature = "llvm15-0")] + let load = builder.build_load(f32_type, arg1, ""); let load_instruction = load.as_instruction_value().unwrap(); assert_eq!(store_instruction.get_volatile().unwrap(), false); @@ -480,7 +486,10 @@ fn test_atomic_ordering_mem_instructions() { let f32_val = f32_type.const_float(::std::f64::consts::PI); let store_instruction = builder.build_store(arg1, f32_val); + #[cfg(not(feature = "llvm15-0"))] let load = builder.build_load(arg1, ""); + #[cfg(feature = "llvm15-0")] + let load = builder.build_load(f32_type, arg1, ""); let load_instruction = load.as_instruction_value().unwrap(); assert_eq!( diff --git a/tests/all/test_passes.rs b/tests/all/test_passes.rs index 78a4d4320a3..ea005fb78fe 100644 --- a/tests/all/test_passes.rs +++ b/tests/all/test_passes.rs @@ -13,6 +13,7 @@ fn test_init_all_passes_for_module() { let module = context.create_module("my_module"); let pass_manager = PassManager::create(()); + #[cfg(not(feature = "llvm15-0"))] pass_manager.add_argument_promotion_pass(); pass_manager.add_constant_merge_pass(); #[cfg(not(any( @@ -30,7 +31,12 @@ fn test_init_all_passes_for_module() { pass_manager.add_always_inliner_pass(); pass_manager.add_global_dce_pass(); pass_manager.add_global_optimizer_pass(); - #[cfg(not(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0")))] + #[cfg(not(any( + feature = "llvm12-0", + feature = "llvm13-0", + feature = "llvm14-0", + feature = "llvm15-0" + )))] pass_manager.add_ip_constant_propagation_pass(); pass_manager.add_prune_eh_pass(); pass_manager.add_ipsccp_pass(); @@ -58,6 +64,7 @@ fn test_init_all_passes_for_module() { pass_manager.add_loop_rotate_pass(); pass_manager.add_loop_reroll_pass(); pass_manager.add_loop_unroll_pass(); + #[cfg(not(feature = "llvm15-0"))] pass_manager.add_loop_unswitch_pass(); pass_manager.add_memcpy_optimize_pass(); pass_manager.add_partially_inline_lib_calls_pass(); @@ -70,9 +77,19 @@ fn test_init_all_passes_for_module() { pass_manager.add_scalar_repl_aggregates_pass_with_threshold(1); pass_manager.add_simplify_lib_calls_pass(); pass_manager.add_tail_call_elimination_pass(); - #[cfg(not(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0")))] + #[cfg(not(any( + feature = "llvm12-0", + feature = "llvm13-0", + feature = "llvm14-0", + feature = "llvm15-0" + )))] pass_manager.add_constant_propagation_pass(); - #[cfg(any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0"))] + #[cfg(any( + feature = "llvm12-0", + feature = "llvm13-0", + feature = "llvm14-0", + feature = "llvm15-0" + ))] pass_manager.add_instruction_simplify_pass(); pass_manager.add_demote_memory_to_register_pass(); pass_manager.add_verifier_pass(); @@ -91,7 +108,13 @@ fn test_init_all_passes_for_module() { pass_manager.add_loop_unroll_and_jam_pass(); } - #[cfg(not(any(feature = "llvm4-0", feature = "llvm5-0", feature = "llvm6-0", feature = "llvm7-0")))] + #[cfg(not(any( + feature = "llvm4-0", + feature = "llvm5-0", + feature = "llvm6-0", + feature = "llvm7-0", + feature = "llvm15-0" + )))] { pass_manager.add_coroutine_early_pass(); pass_manager.add_coroutine_split_pass(); @@ -142,18 +165,20 @@ fn test_pass_manager_builder() { pass_manager_builder.populate_module_pass_manager(&module_pass_manager); - let module2 = module.clone(); + #[cfg(not(feature = "llvm15-0"))] + { + let module2 = module.clone(); + + let lto_pass_manager = PassManager::create(()); + pass_manager_builder.populate_lto_pass_manager(<o_pass_manager, false, false); + + assert!(lto_pass_manager.run_on(&module2)); + } #[cfg(any(feature = "llvm4-0", feature = "llvm5-0"))] assert!(!module_pass_manager.run_on(&module)); #[cfg(not(any(feature = "llvm4-0", feature = "llvm5-0")))] assert!(module_pass_manager.run_on(&module)); - - let lto_pass_manager = PassManager::create(()); - - pass_manager_builder.populate_lto_pass_manager(<o_pass_manager, false, false); - - assert!(lto_pass_manager.run_on(&module2)); } #[test] diff --git a/tests/all/test_types.rs b/tests/all/test_types.rs index e568694189e..9cd6bdda5a8 100644 --- a/tests/all/test_types.rs +++ b/tests/all/test_types.rs @@ -312,7 +312,16 @@ fn test_const_zero() { struct_zero.print_to_string().to_str(), Ok("{ i8, fp128 } zeroinitializer") ); - assert_eq!(ptr_zero.print_to_string().to_str(), Ok("double* null")); + + // handle opaque pointers + let ptr_type = if cfg!(any(feature = "llvm15-0")) { + "ptr null" + } else { + "double* null" + }; + + assert_eq!(ptr_zero.print_to_string().to_str(), Ok(ptr_type)); + assert_eq!(vec_zero.print_to_string().to_str(), Ok("<42 x double> zeroinitializer")); assert_eq!( array_zero.print_to_string().to_str(), @@ -345,6 +354,8 @@ fn test_ptr_type() { let ptr_type = i8_type.ptr_type(AddressSpace::default()); assert_eq!(ptr_type.get_address_space(), AddressSpace::default()); + + #[cfg(not(feature = "llvm15-0"))] assert_eq!(ptr_type.get_element_type().into_int_type(), i8_type); // Fn ptr: @@ -352,7 +363,9 @@ fn test_ptr_type() { let fn_type = void_type.fn_type(&[], false); let fn_ptr_type = fn_type.ptr_type(AddressSpace::default()); + #[cfg(not(feature = "llvm15-0"))] assert_eq!(fn_ptr_type.get_element_type().into_function_type(), fn_type); + assert_eq!(fn_ptr_type.get_context(), context); } diff --git a/tests/all/test_values.rs b/tests/all/test_values.rs index 958e0f97441..f59cb0fbcd0 100644 --- a/tests/all/test_values.rs +++ b/tests/all/test_values.rs @@ -4,8 +4,8 @@ use inkwell::comdat::ComdatSelectionKind; use inkwell::context::Context; use inkwell::module::Linkage::*; use inkwell::types::{StringRadix, VectorType}; -use inkwell::values::{AnyValue, CallableValue, InstructionOpcode::*, FIRST_CUSTOM_METADATA_KIND_ID}; -use inkwell::{AddressSpace, DLLStorageClass, FloatPredicate, GlobalVisibility, ThreadLocalMode}; +use inkwell::values::{AnyValue, InstructionOpcode::*, FIRST_CUSTOM_METADATA_KIND_ID}; +use inkwell::{AddressSpace, DLLStorageClass, GlobalVisibility, ThreadLocalMode}; use std::convert::TryFrom; @@ -38,7 +38,6 @@ fn test_call_site() { let fn_type = void_type.fn_type(&[], false); let function = module.add_function("do_nothing", fn_type, None); - let call_site = builder.build_call(function, &[], "to_infinity_and_beyond"); assert_eq!(call_site.count_arguments(), 0); @@ -522,77 +521,82 @@ fn test_metadata() { #[test] fn test_floats() { - let context = Context::create(); + #[cfg(not(feature = "llvm15-0"))] + { + use inkwell::FloatPredicate; - let f32_type = context.f32_type(); - let f64_type = context.f64_type(); - let f128_type = context.f128_type(); - let i64_type = context.i32_type(); + let context = Context::create(); - let f64_pi = f64_type.const_float(::std::f64::consts::PI); + let f32_type = context.f32_type(); + let f64_type = context.f64_type(); + let f128_type = context.f128_type(); + let i64_type = context.i32_type(); + + let f64_pi = f64_type.const_float(::std::f64::consts::PI); - let f32_pi = f64_pi.const_truncate(f32_type); - let f128_pi = f64_pi.const_extend(f128_type); - let i64_pi = f64_pi.const_to_signed_int(i64_type); - let u64_pi = f64_pi.const_to_unsigned_int(i64_type); - let f128_pi_cast = f64_pi.const_cast(f128_type); + let f32_pi = f64_pi.const_truncate(f32_type); + let f128_pi = f64_pi.const_extend(f128_type); + let i64_pi = f64_pi.const_to_signed_int(i64_type); + let u64_pi = f64_pi.const_to_unsigned_int(i64_type); + let f128_pi_cast = f64_pi.const_cast(f128_type); - assert_eq!(i64_pi.get_type(), i64_type); - assert_eq!(u64_pi.get_type(), i64_type); - assert_eq!(f32_pi.get_type(), f32_type); - assert_eq!(f128_pi.get_type(), f128_type); - assert_eq!(f128_pi_cast.get_type(), f128_type); + assert_eq!(i64_pi.get_type(), i64_type); + assert_eq!(u64_pi.get_type(), i64_type); + assert_eq!(f32_pi.get_type(), f32_type); + assert_eq!(f128_pi.get_type(), f128_type); + assert_eq!(f128_pi_cast.get_type(), f128_type); - // REIVEW: Why are these not FPTrunc, FPExt, FPToSI, FPToUI, BitCast instructions? - // Only thing I can think of is that they're constants and therefore precalculated - assert!(f32_pi.as_instruction().is_none()); - assert!(f128_pi.as_instruction().is_none()); - assert!(i64_pi.as_instruction().is_none()); - assert!(u64_pi.as_instruction().is_none()); - assert!(f128_pi_cast.as_instruction().is_none()); + // REIVEW: Why are these not FPTrunc, FPExt, FPToSI, FPToUI, BitCast instructions? + // Only thing I can think of is that they're constants and therefore precalculated + assert!(f32_pi.as_instruction().is_none()); + assert!(f128_pi.as_instruction().is_none()); + assert!(i64_pi.as_instruction().is_none()); + assert!(u64_pi.as_instruction().is_none()); + assert!(f128_pi_cast.as_instruction().is_none()); - let f64_one = f64_type.const_float(1.); - let f64_two = f64_type.const_float(2.); - let neg_two = f64_two.const_neg(); + let f64_one = f64_type.const_float(1.); + let f64_two = f64_type.const_float(2.); + let neg_two = f64_two.const_neg(); - assert_eq!(neg_two.print_to_string().to_str(), Ok("double -2.000000e+00")); + assert_eq!(neg_two.print_to_string().to_str(), Ok("double -2.000000e+00")); - let neg_three = neg_two.const_sub(f64_one); + let neg_three = neg_two.const_sub(f64_one); - assert_eq!(neg_three.print_to_string().to_str(), Ok("double -3.000000e+00")); + assert_eq!(neg_three.print_to_string().to_str(), Ok("double -3.000000e+00")); - let pos_six = neg_three.const_mul(neg_two); + let pos_six = neg_three.const_mul(neg_two); - assert_eq!(pos_six.print_to_string().to_str(), Ok("double 6.000000e+00")); + assert_eq!(pos_six.print_to_string().to_str(), Ok("double 6.000000e+00")); - let pos_eight = pos_six.const_add(f64_two); + let pos_eight = pos_six.const_add(f64_two); - assert_eq!(pos_eight.print_to_string().to_str(), Ok("double 8.000000e+00")); + assert_eq!(pos_eight.print_to_string().to_str(), Ok("double 8.000000e+00")); - let pos_four = pos_eight.const_div(f64_two); + let pos_four = pos_eight.const_div(f64_two); - assert_eq!(pos_four.print_to_string().to_str(), Ok("double 4.000000e+00")); + assert_eq!(pos_four.print_to_string().to_str(), Ok("double 4.000000e+00")); - let rem = pos_six.const_remainder(pos_four); + let rem = pos_six.const_remainder(pos_four); - assert_eq!(rem.print_to_string().to_str(), Ok("double 2.000000e+00")); + assert_eq!(rem.print_to_string().to_str(), Ok("double 2.000000e+00")); - assert!(f64_one.const_compare(FloatPredicate::PredicateFalse, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::PredicateTrue, f64_two).is_null()); - assert!(f64_one.const_compare(FloatPredicate::OEQ, f64_two).is_null()); - assert!(f64_one.const_compare(FloatPredicate::OGT, f64_two).is_null()); - assert!(f64_one.const_compare(FloatPredicate::OGE, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::OLT, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::OLE, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::ONE, f64_two).is_null()); - assert!(f64_one.const_compare(FloatPredicate::UEQ, f64_two).is_null()); - assert!(f64_one.const_compare(FloatPredicate::UGT, f64_two).is_null()); - assert!(f64_one.const_compare(FloatPredicate::UGE, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::ULT, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::ULE, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::UNE, f64_two).is_null()); - assert!(!f64_one.const_compare(FloatPredicate::ORD, f64_two).is_null()); - assert!(f64_one.const_compare(FloatPredicate::UNO, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::PredicateFalse, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::PredicateTrue, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::OEQ, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::OGT, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::OGE, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::OLT, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::OLE, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::ONE, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::UEQ, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::UGT, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::UGE, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::ULT, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::ULE, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::UNE, f64_two).is_null()); + assert!(!f64_one.const_compare(FloatPredicate::ORD, f64_two).is_null()); + assert!(f64_one.const_compare(FloatPredicate::UNO, f64_two).is_null()); + } } #[test] @@ -917,6 +921,9 @@ fn test_allocations() { builder.position_at_end(entry_block); + // handle opaque pointers + let ptr_type = if cfg!(any(feature = "llvm15-0")) { "ptr" } else { "i32*" }; + // REVIEW: Alloca (and possibly malloc) seem to be prone to segfaulting // when called with a builder that isn't positioned. I wonder if other // builder methods have this problem? We could make builder subtypes: @@ -926,21 +933,21 @@ fn test_allocations() { let stack_ptr = builder.build_alloca(i32_type, "stack_ptr"); - assert_eq!(stack_ptr.get_type().print_to_string().to_str(), Ok("i32*")); + assert_eq!(stack_ptr.get_type().print_to_string().to_str(), Ok(ptr_type)); let stack_array = builder.build_array_alloca(i32_type, i32_three, "stack_array"); - assert_eq!(stack_array.get_type().print_to_string().to_str(), Ok("i32*")); + assert_eq!(stack_array.get_type().print_to_string().to_str(), Ok(ptr_type)); let heap_ptr = builder.build_malloc(i32_type, "heap_ptr"); assert!(heap_ptr.is_ok()); - assert_eq!(heap_ptr.unwrap().get_type().print_to_string().to_str(), Ok("i32*")); + assert_eq!(heap_ptr.unwrap().get_type().print_to_string().to_str(), Ok(ptr_type)); let heap_array = builder.build_array_malloc(i32_type, i32_three, "heap_array"); assert!(heap_array.is_ok()); - assert_eq!(heap_array.unwrap().get_type().print_to_string().to_str(), Ok("i32*")); + assert_eq!(heap_array.unwrap().get_type().print_to_string().to_str(), Ok(ptr_type)); let bad_malloc_res = builder.build_malloc(unsized_type, ""); @@ -1141,8 +1148,14 @@ fn test_non_fn_ptr_called() { let i8_ptr_param = fn_value.get_first_param().unwrap().into_pointer_value(); builder.position_at_end(bb); - let callable_value = CallableValue::try_from(i8_ptr_param).unwrap(); - builder.build_call(callable_value, &[], "call"); + #[cfg(not(feature = "llvm15-0"))] + { + use inkwell::values::CallableValue; + let callable_value = CallableValue::try_from(i8_ptr_param).unwrap(); + builder.build_call(callable_value, &[], "call"); + } + #[cfg(feature = "llvm15-0")] + builder.build_indirect_call(i8_ptr_type.fn_type(&[], false), i8_ptr_param, &[], "call"); builder.build_return(None); assert!(module.verify().is_ok()); @@ -1189,7 +1202,10 @@ fn test_aggregate_returns() { let ptr_param2 = fn_value.get_nth_param(1).unwrap().into_pointer_value(); builder.position_at_end(bb); + #[cfg(not(feature = "llvm15-0"))] builder.build_ptr_diff(ptr_param1, ptr_param2, "diff"); + #[cfg(feature = "llvm15-0")] + builder.build_ptr_diff(i32_ptr_type, ptr_param1, ptr_param2, "diff"); builder.build_aggregate_return(&[i32_three.into(), i32_seven.into()]); assert!(module.verify().is_ok());