use crate::constants;
use crate::function;
use crate::instruction::{self, ICmpPred, Opcode};
use crate::module::Module;
use crate::types::{Type, TypeId, TypeStore};
use crate::value::{valref, SubclassKind, Value, ValueRef};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
KwDeclare,
KwDefine,
KwTarget,
KwTriple,
KwDatalayout,
KwSourceFilename,
KwGlobal,
KwConstant,
KwType,
KwAttributes,
KwComdat,
KwMetadata,
KwNull,
KwUndef,
KwPoison,
KwZeroinitializer,
KwTrue,
KwFalse,
KwVoid,
KwLabel,
KwPtr,
KwAddrspace,
KwVscale,
KwC,
KwX,
KwTo,
KwToken,
KwUnreachable,
KwRet,
KwBr,
KwSwitch,
KwIndirectBr,
KwInvoke,
KwCallBr,
KwResume,
KwCatchSwitch,
KwCatchRet,
KwCleanupRet,
KwAdd,
KwSub,
KwMul,
KwUDiv,
KwSDiv,
KwURem,
KwSRem,
KwFAdd,
KwFSub,
KwFMul,
KwFDiv,
KwFRem,
KwShl,
KwLShr,
KwAShr,
KwAnd,
KwOr,
KwXor,
KwAlloca,
KwLoad,
KwStore,
KwGetElementPtr,
KwFence,
KwCmpXchg,
KwAtomicRMW,
KwTrunc,
KwZExt,
KwSExt,
KwFPTrunc,
KwFPExt,
KwFPToUI,
KwFPToSI,
KwUIToFP,
KwSIToFP,
KwPtrToInt,
KwIntToPtr,
KwBitCast,
KwAddrSpaceCast,
KwICmp,
KwFCmp,
KwCall,
KwPhi,
KwSelect,
KwFreeze,
KwVAArg,
KwExtractElement,
KwInsertElement,
KwShuffleVector,
KwExtractValue,
KwInsertValue,
KwLandingPad,
KwCatchPad,
KwCleanupPad,
KwNoInline,
KwAlwaysInline,
KwNoReturn,
KwNoUnwind,
KwReadNone,
KwReadOnly,
KwWriteOnly,
KwArgMemOnly,
KwInaccessibleMemOnly,
KwInaccessibleMemOrArgMemOnly,
KwInBounds,
KwNuw,
KwNsw,
KwExact,
KwAtomic,
KwAcquire,
KwRelease,
KwAcqRel,
KwSeqCst,
KwMonotonic,
KwDsoLocal,
KwDsoPreemptable,
KwThreadLocal,
KwUnnamedAddr,
KwExternallyInitialized,
KwDllStorageClass,
KwSection,
KwPartition,
KwSanitizeAddress,
KwSanitizeThread,
KwSanitizeMemory,
KwSanitizeHwaddress,
KwNoSanitize,
KwNoCfCheck,
KwOptNone,
KwOptForFuzzing,
KwShadowCallStack,
KwSpeculativeLoadHardening,
KwImmArg,
LParen, RParen, LBrace, RBrace, LBracket, RBracket, LAngle, RAngle, Comma, Equal, Star, Exclaim, Hash, Semicolon, DotDot, DotDotDot,
GlobalIdent(String), LocalIdent(String), LabelIdent(String), IntLit(i64), FloatLit(f64), StringLit(String), MetadataId(u64), MetadataName(String), AttrGroupId(u64), }
pub struct Lexer<'a> {
input: &'a str,
pos: usize,
}
impl<'a> Lexer<'a> {
pub fn new(input: &'a str) -> Self {
Self { input, pos: 0 }
}
pub fn tokenize(&mut self) -> Vec<Token> {
let mut tokens = Vec::new();
while self.pos < self.input.len() {
self.skip_whitespace();
if self.pos >= self.input.len() {
break;
}
let ch = self.cur();
if ch == ';' {
self.skip_line();
continue;
}
if ch == '!' {
self.advance();
tokens.push(self.lex_metadata());
continue;
}
if ch == '#' {
self.advance();
tokens.push(self.lex_attr_group());
continue;
}
if ch == '@' {
self.advance();
let name = self.lex_name();
tokens.push(Token::GlobalIdent(name));
continue;
}
if ch == '%' {
self.advance();
let name = self.lex_name();
tokens.push(Token::LocalIdent(name));
continue;
}
if ch == '"' {
tokens.push(Token::StringLit(self.lex_string()));
continue;
}
if ch.is_ascii_digit() || (ch == '-' && self.peek().is_some_and(|c| c.is_ascii_digit()))
{
tokens.push(self.lex_number());
continue;
}
match ch {
'(' => {
self.advance();
tokens.push(Token::LParen);
}
')' => {
self.advance();
tokens.push(Token::RParen);
}
'{' => {
self.advance();
tokens.push(Token::LBrace);
}
'}' => {
self.advance();
tokens.push(Token::RBrace);
}
'[' => {
self.advance();
tokens.push(Token::LBracket);
}
']' => {
self.advance();
tokens.push(Token::RBracket);
}
'<' => {
self.advance();
tokens.push(Token::LAngle);
}
'>' => {
self.advance();
tokens.push(Token::RAngle);
}
',' => {
self.advance();
tokens.push(Token::Comma);
}
'=' => {
self.advance();
tokens.push(Token::Equal);
}
'*' => {
self.advance();
tokens.push(Token::Star);
}
_ if ch.is_alphabetic() || ch == '_' => {
tokens.push(self.lex_keyword_or_label());
}
_ => {
self.advance();
} }
}
tokens
}
fn cur(&self) -> char {
self.input[self.pos..].chars().next().unwrap_or('\0')
}
fn advance(&mut self) {
if self.pos < self.input.len() {
self.pos += self.cur().len_utf8();
}
}
fn peek(&self) -> Option<char> {
let p = self.pos + self.cur().len_utf8();
if p < self.input.len() {
Some(self.input[p..].chars().next().unwrap())
} else {
None
}
}
fn skip_whitespace(&mut self) {
while self.pos < self.input.len() {
let ch = self.cur();
if ch.is_whitespace() {
self.advance();
} else {
break;
}
}
}
fn skip_line(&mut self) {
while self.pos < self.input.len() && self.cur() != '\n' {
self.advance();
}
}
fn lex_name(&mut self) -> String {
let start = self.pos;
while self.pos < self.input.len() {
let ch = self.cur();
if ch.is_alphanumeric() || ch == '_' || ch == '.' || ch == '$' || ch == '-' {
self.advance();
} else {
break;
}
}
self.input[start..self.pos].to_string()
}
fn lex_string(&mut self) -> String {
self.advance(); let start = self.pos;
while self.pos < self.input.len() && self.cur() != '"' {
if self.cur() == '\\' {
self.advance(); }
self.advance();
}
let s = self.input[start..self.pos].to_string();
if self.pos < self.input.len() {
self.advance(); }
s
}
fn lex_metadata(&mut self) -> Token {
if self.cur().is_ascii_digit() {
let n = self.lex_integer();
Token::MetadataId(n as u64)
} else {
let name = self.lex_name();
Token::MetadataName(name)
}
}
fn lex_attr_group(&mut self) -> Token {
let n = self.lex_integer();
Token::AttrGroupId(n as u64)
}
fn lex_integer(&mut self) -> i64 {
let start = self.pos;
if self.cur() == '-' {
self.advance();
}
while self.pos < self.input.len() && self.cur().is_ascii_digit() {
self.advance();
}
self.input[start..self.pos].parse().unwrap_or(0)
}
fn lex_number(&mut self) -> Token {
let start = self.pos;
let neg = self.cur() == '-';
if neg {
self.advance();
}
while self.pos < self.input.len() && self.cur().is_ascii_digit() {
self.advance();
}
if self.pos < self.input.len() && self.cur() == '.' {
self.advance();
while self.pos < self.input.len() && self.cur().is_ascii_digit() {
self.advance();
}
let v: f64 = self.input[start..self.pos].parse().unwrap_or(0.0);
Token::FloatLit(v)
} else {
let v: i64 = self.input[start..self.pos].parse().unwrap_or(0);
Token::IntLit(v)
}
}
fn lex_keyword_or_label(&mut self) -> Token {
let s = self.lex_name();
let saved = self.pos;
self.skip_whitespace();
if self.pos < self.input.len() && self.cur() == ':' {
self.pos = saved;
return Token::LabelIdent(s);
}
self.pos = saved;
match s.as_str() {
"declare" => Token::KwDeclare,
"define" => Token::KwDefine,
"target" => Token::KwTarget,
"triple" => Token::KwTriple,
"datalayout" => Token::KwDatalayout,
"source_filename" => Token::KwSourceFilename,
"global" => Token::KwGlobal,
"constant" => Token::KwConstant,
"type" => Token::KwType,
"attributes" => Token::KwAttributes,
"comdat" => Token::KwComdat,
"metadata" => Token::KwMetadata,
"null" => Token::KwNull,
"undef" => Token::KwUndef,
"poison" => Token::KwPoison,
"zeroinitializer" => Token::KwZeroinitializer,
"true" => Token::KwTrue,
"false" => Token::KwFalse,
"void" => Token::KwVoid,
"ptr" => Token::KwPtr,
"addrspace" => Token::KwAddrspace,
"vscale" => Token::KwVscale,
"c" => Token::KwC,
"x" => Token::KwX,
"to" => Token::KwTo,
"token" => Token::KwToken,
"unreachable" => Token::KwUnreachable,
"ret" => Token::KwRet,
"br" => Token::KwBr,
"switch" => Token::KwSwitch,
"indirectbr" => Token::KwIndirectBr,
"invoke" => Token::KwInvoke,
"callbr" => Token::KwCallBr,
"resume" => Token::KwResume,
"catchswitch" => Token::KwCatchSwitch,
"catchret" => Token::KwCatchRet,
"cleanupret" => Token::KwCleanupRet,
"add" => Token::KwAdd,
"sub" => Token::KwSub,
"mul" => Token::KwMul,
"udiv" => Token::KwUDiv,
"sdiv" => Token::KwSDiv,
"urem" => Token::KwURem,
"srem" => Token::KwSRem,
"fadd" => Token::KwFAdd,
"fsub" => Token::KwFSub,
"fmul" => Token::KwFMul,
"fdiv" => Token::KwFDiv,
"frem" => Token::KwFRem,
"shl" => Token::KwShl,
"lshr" => Token::KwLShr,
"ashr" => Token::KwAShr,
"and" => Token::KwAnd,
"or" => Token::KwOr,
"xor" => Token::KwXor,
"alloca" => Token::KwAlloca,
"load" => Token::KwLoad,
"store" => Token::KwStore,
"getelementptr" => Token::KwGetElementPtr,
"fence" => Token::KwFence,
"cmpxchg" => Token::KwCmpXchg,
"atomicrmw" => Token::KwAtomicRMW,
"trunc" => Token::KwTrunc,
"zext" => Token::KwZExt,
"sext" => Token::KwSExt,
"fptrunc" => Token::KwFPTrunc,
"fpext" => Token::KwFPExt,
"fptoui" => Token::KwFPToUI,
"fptosi" => Token::KwFPToSI,
"uitofp" => Token::KwUIToFP,
"sitofp" => Token::KwSIToFP,
"ptrtoint" => Token::KwPtrToInt,
"inttoptr" => Token::KwIntToPtr,
"bitcast" => Token::KwBitCast,
"addrspacecast" => Token::KwAddrSpaceCast,
"icmp" => Token::KwICmp,
"fcmp" => Token::KwFCmp,
"call" => Token::KwCall,
"phi" => Token::KwPhi,
"select" => Token::KwSelect,
"freeze" => Token::KwFreeze,
"va_arg" => Token::KwVAArg,
"extractelement" => Token::KwExtractElement,
"insertelement" => Token::KwInsertElement,
"shufflevector" => Token::KwShuffleVector,
"extractvalue" => Token::KwExtractValue,
"insertvalue" => Token::KwInsertValue,
"landingpad" => Token::KwLandingPad,
"catchpad" => Token::KwCatchPad,
"cleanuppad" => Token::KwCleanupPad,
"noinline" => Token::KwNoInline,
"alwaysinline" => Token::KwAlwaysInline,
"noreturn" => Token::KwNoReturn,
"nounwind" => Token::KwNoUnwind,
"readnone" => Token::KwReadNone,
"readonly" => Token::KwReadOnly,
"writeonly" => Token::KwWriteOnly,
"argmemonly" => Token::KwArgMemOnly,
"inaccessiblememonly" => Token::KwInaccessibleMemOnly,
"inaccessiblemem_or_argmemonly" => Token::KwInaccessibleMemOrArgMemOnly,
"inbounds" => Token::KwInBounds,
"nuw" => Token::KwNuw,
"nsw" => Token::KwNsw,
"exact" => Token::KwExact,
"atomic" => Token::KwAtomic,
"acquire" => Token::KwAcquire,
"release" => Token::KwRelease,
"acq_rel" => Token::KwAcqRel,
"seq_cst" => Token::KwSeqCst,
"monotonic" => Token::KwMonotonic,
"dso_local" => Token::KwDsoLocal,
"dso_preemptable" => Token::KwDsoPreemptable,
"thread_local" => Token::KwThreadLocal,
"unnamed_addr" => Token::KwUnnamedAddr,
"externally_initialized" => Token::KwExternallyInitialized,
"dllimport" | "dllexport" => Token::KwDllStorageClass,
"section" => Token::KwSection,
"partition" => Token::KwPartition,
"sanitize_address" => Token::KwSanitizeAddress,
"sanitize_thread" => Token::KwSanitizeThread,
"sanitize_memory" => Token::KwSanitizeMemory,
"sanitize_hwaddress" => Token::KwSanitizeHwaddress,
"nosanitize" => Token::KwNoSanitize,
"nocf_check" => Token::KwNoCfCheck,
"optnone" => Token::KwOptNone,
"optforfuzzing" => Token::KwOptForFuzzing,
"shadowcallstack" => Token::KwShadowCallStack,
"speculatable" => Token::KwSpeculativeLoadHardening,
"immarg" => Token::KwImmArg,
_ => Token::LabelIdent(s),
}
}
}
pub struct Parser<'a> {
tokens: &'a [Token],
pos: usize,
pub type_store: TypeStore,
pub named_types: HashMap<String, TypeId>,
pub globals: HashMap<String, ValueRef>,
pub locals: HashMap<String, ValueRef>,
pub module: Module,
pub errors: Vec<String>,
}
impl<'a> Parser<'a> {
pub fn new(tokens: &'a [Token]) -> Self {
Self {
tokens,
pos: 0,
type_store: TypeStore::new(),
named_types: HashMap::new(),
globals: HashMap::new(),
locals: HashMap::new(),
module: Module::new("parsed"),
errors: Vec::new(),
}
}
fn peek(&self) -> Option<&Token> {
self.tokens.get(self.pos)
}
fn peek_n(&self, n: usize) -> Option<&Token> {
self.tokens.get(self.pos + n)
}
fn advance(&mut self) -> Option<&Token> {
let t = self.tokens.get(self.pos);
self.pos += 1;
if self.pos > 100_000 {
panic!("Parser exceeded token limit");
}
t
}
fn expect(&mut self, expected: Token) -> bool {
match self.peek() {
Some(t) if *t == expected => {
self.advance();
true
}
Some(t) => {
self.errors
.push(format!("Expected {:?}, got {:?}", expected, t));
false
}
None => {
self.errors
.push(format!("Expected {:?}, got EOF", expected));
false
}
}
}
fn consume_if(&mut self, expected: &Token) -> bool {
match self.peek() {
Some(t) if t == expected => {
self.advance();
true
}
_ => false,
}
}
#[allow(dead_code)]
fn error(&mut self, msg: impl Into<String>) {
self.errors.push(msg.into());
}
pub fn parse_module(&mut self) -> Module {
while self.peek().is_some() {
match self.peek().unwrap() {
Token::KwTarget => {
self.advance();
if self.peek() == Some(&Token::KwTriple) {
self.advance();
self.expect(Token::Equal);
let triple_val = self.advance().cloned();
if let Some(Token::StringLit(s)) = triple_val {
self.module.set_target_triple(&s);
}
} else if self.peek() == Some(&Token::KwDatalayout) {
self.advance();
self.expect(Token::Equal);
let dl_val = self.advance().cloned();
if let Some(Token::StringLit(s)) = dl_val {
self.module.set_data_layout(&s);
}
}
}
Token::KwSourceFilename => {
self.advance();
self.expect(Token::Equal);
let src_val = self.advance().cloned();
if let Some(Token::StringLit(s)) = src_val {
self.module.source_filename = s;
}
}
Token::KwDeclare => {
self.advance();
self.parse_declaration();
}
Token::KwDefine => {
self.advance();
self.parse_definition();
}
Token::GlobalIdent(_) => {
self.parse_global_variable();
}
Token::LocalIdent(_) => {
self.parse_type_definition();
}
Token::KwAttributes => {
self.advance();
self.parse_attr_group();
}
Token::MetadataName(_) | Token::KwMetadata => {
self.parse_named_metadata();
}
Token::LabelIdent(s) if s.starts_with('$') => {
self.advance();
self.parse_comdat();
}
_ => {
self.errors
.push(format!("Unexpected token: {:?}", self.peek().unwrap()));
self.advance();
}
}
}
self.module.clone()
}
fn parse_type_definition(&mut self) {
if let Some(Token::LocalIdent(name)) = self.advance() {
let saved_name = name.clone();
if self.peek() == Some(&Token::Equal) {
self.advance();
if self.consume_if(&Token::KwType) {
if let Some(ty) = self.parse_type() {
let unique = self.type_store.insert_or_get(ty);
self.named_types.insert(saved_name, unique.id);
} else {
self.advance();
}
}
}
}
}
fn parse_type(&mut self) -> Option<Type> {
match self.peek()? {
Token::KwVoid => {
self.advance();
Some(Type::void())
}
Token::KwPtr => {
self.advance();
if self.consume_if(&Token::KwAddrspace) {
self.expect(Token::LParen);
let addr = self.parse_int_lit().unwrap_or(0) as u32;
self.expect(Token::RParen);
Some(Type::pointer(addr))
} else {
Some(Type::pointer(0))
}
}
Token::KwLabel => {
self.advance();
Some(Type::label())
}
Token::KwMetadata => {
self.advance();
Some(Type::metadata())
}
Token::KwToken => {
self.advance();
Some(Type::token())
}
Token::LabelIdent(s) if s.starts_with('i') => {
let s = s.clone();
self.advance();
if let Ok(bits) = s[1..].parse::<u32>() {
Some(Type::int(bits))
} else {
None
}
}
Token::LabelIdent(s) => {
let s = s.clone();
self.advance();
match s.as_str() {
"half" => Some(Type::half()),
"bfloat" => Some(Type::bfloat()),
"float" => Some(Type::float()),
"double" => Some(Type::double()),
"fp128" => Some(Type::fp128()),
"x86_fp80" => Some(Type::x86_fp80()),
"ppc_fp128" => Some(Type::ppc_fp128()),
_ => None,
}
}
Token::LocalIdent(name) => {
let name = name.clone();
self.advance();
if let Some(&_id) = self.named_types.get(&name) {
Some(Type::struct_opaque(name)) } else {
None
}
}
Token::LBracket => self.parse_array_type(),
Token::LAngle => self.parse_vector_type(),
Token::LBrace => {
self.parse_struct_type(false)
}
Token::LabelIdent(s)
if s.starts_with('i')
|| matches!(
s.as_str(),
"half" | "bfloat" | "float" | "double" | "fp128" | "x86_fp80" | "ppc_fp128"
) =>
{
let ret_ty = self.parse_type()?;
if self.peek() == Some(&Token::LParen) {
self.parse_function_type(Some(ret_ty))
} else {
Some(ret_ty)
}
}
_ => None,
}
}
fn parse_struct_type(&mut self, is_packed: bool) -> Option<Type> {
self.expect(Token::LBrace);
let mut elements = Vec::new();
while self.peek() != Some(&Token::RBrace) {
if let Some(ty) = self.parse_type() {
elements.push(ty.id);
} else {
break;
}
self.consume_if(&Token::Comma);
}
self.expect(Token::RBrace);
if is_packed {
self.expect(Token::RAngle);
}
let st = Type::struct_literal_with(is_packed, elements);
Some(self.type_store.insert_or_get(st))
}
fn parse_vector_type(&mut self) -> Option<Type> {
self.advance(); if self.consume_if(&Token::KwVscale) {
self.expect(Token::KwX);
let min_elems = self.parse_int_lit()? as u32;
self.expect(Token::KwX);
let elem_ty = self.parse_type()?;
self.expect(Token::RAngle);
let elem_id = elem_ty.id;
let sv = Type::scalable_vector_with(min_elems, elem_id);
Some(self.type_store.insert_or_get(sv))
} else {
let len = self.parse_int_lit()? as u32;
self.expect(Token::KwX);
let elem_ty = self.parse_type()?;
self.expect(Token::RAngle);
let elem_id = elem_ty.id;
let fv = Type::fixed_vector_with(len, elem_id);
Some(self.type_store.insert_or_get(fv))
}
}
fn parse_array_type(&mut self) -> Option<Type> {
self.advance(); let len = self.parse_int_lit()? as u64;
self.expect(Token::KwX);
let elem_ty = self.parse_type()?;
self.expect(Token::RBracket);
let elem_id = elem_ty.id;
let arr = Type::array_with(len, elem_id);
Some(self.type_store.insert_or_get(arr))
}
fn parse_pointer_type(&mut self) -> Option<Type> {
self.advance(); if self.consume_if(&Token::KwAddrspace) {
self.expect(Token::LParen);
let addr = self.parse_int_lit().unwrap_or(0) as u32;
self.expect(Token::RParen);
Some(Type::pointer(addr))
} else {
Some(Type::pointer(0))
}
}
fn parse_function_type(&mut self, ret_ty: Option<Type>) -> Option<Type> {
let rty = match ret_ty {
Some(rt) => rt,
None => Type::void(),
};
self.expect(Token::LParen);
let mut params = Vec::new();
let mut is_vararg = false;
while self.peek() != Some(&Token::RParen) {
if self.consume_if(&Token::DotDotDot) {
is_vararg = true;
break;
}
if let Some(ty) = self.parse_type() {
params.push(ty.id);
} else {
break;
}
self.consume_if(&Token::Comma);
}
self.expect(Token::RParen);
let ft = Type::function_type_with(rty.id, params, is_vararg);
Some(self.type_store.insert_or_get(ft))
}
fn parse_named_type(&mut self) -> Option<Type> {
match self.peek()? {
Token::LocalIdent(name) => {
let name = name.clone();
self.advance();
if self.named_types.contains_key(&name) {
Some(Type::struct_opaque(name))
} else {
None
}
}
_ => None,
}
}
fn parse_declaration(&mut self) {
if let Some(_ret_ty) = self.parse_type() {
self.parse_fn_attributes();
if let Some(Token::GlobalIdent(name)) = self.advance() {
let saved_name = name.clone();
self.expect(Token::LParen);
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.advance().unwrap() {
Token::LParen => depth += 1,
Token::RParen => depth -= 1,
_ => {}
}
}
self.parse_return_attributes();
let func = function::new_function(&saved_name, Type::void(), &[]);
func.borrow_mut().name = saved_name;
self.module.add_function(func);
}
}
}
fn parse_definition(&mut self) {
self.locals.clear();
let ret_ty = self.parse_type();
self.parse_fn_attributes();
if let Some(Token::GlobalIdent(name)) = self.advance() {
let func_name = name.clone();
self.expect(Token::LParen);
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.advance().unwrap() {
Token::LParen => depth += 1,
Token::RParen => depth -= 1,
_ => {}
}
}
self.parse_return_attributes();
let func_ty = match ret_ty {
Some(rt) => rt,
None => Type::void(),
};
let func = function::new_function(&func_name, func_ty, &[]);
func.borrow_mut().name = func_name;
if self.consume_if(&Token::LBrace) {
self.parse_function_body(&func);
}
self.module.add_function(func);
}
}
fn parse_function_body(&mut self, func: &ValueRef) {
let mut current_block: Option<ValueRef> = None;
loop {
match self.peek() {
Some(Token::RBrace) | None => {
self.advance();
break;
}
_ => {}
}
if let Some(Token::LabelIdent(label)) = self.peek().cloned() {
let is_label_start = matches!(self.peek(), Some(Token::LabelIdent(_)));
if is_label_start {
self.advance(); let bb = crate::basic_block::new_basic_block(&label);
func.borrow_mut().push_operand(bb.clone());
current_block = Some(bb);
continue;
}
}
let has_assignment = matches!(self.peek(), Some(Token::LocalIdent(_)))
&& matches!(self.peek_n(1), Some(Token::Equal));
let result_name = if has_assignment {
if let Some(Token::LocalIdent(name)) = self.advance() {
let n = name.clone();
self.advance(); Some(n)
} else {
None
}
} else {
None
};
match self.parse_instruction(result_name.as_deref()) { Some(inst) => {
if let Some(ref bb) = current_block {
bb.borrow_mut().push_operand(inst);
}
} _ => {
if self.peek().is_some() {
self.advance();
}
}}
}
}
fn parse_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
match self.peek()? {
Token::KwRet => {
self.advance();
if self.peek() == Some(&Token::KwVoid) {
self.advance();
let inst = instruction::ret_void();
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
} else {
self.parse_type(); let val = self.parse_value()?;
let inst = instruction::ret_val(val);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
}
Token::KwBr => {
self.advance();
if self.peek() == Some(&Token::KwLabel) {
self.advance();
if let Some(Token::LocalIdent(label)) = self.advance() {
let block = crate::basic_block::new_basic_block(label);
Some(instruction::br(block))
} else {
None
}
} else {
let cond = self.parse_value()?;
self.expect(Token::Comma);
self.expect(Token::KwLabel);
let true_label_tok = self.advance().cloned();
if let Some(Token::LocalIdent(true_label)) = true_label_tok {
self.expect(Token::Comma);
self.expect(Token::KwLabel);
let false_label_tok = self.advance().cloned();
if let Some(Token::LocalIdent(false_label)) = false_label_tok {
let t_bb = crate::basic_block::new_basic_block(&true_label);
let f_bb = crate::basic_block::new_basic_block(&false_label);
Some(instruction::br_cond(cond, t_bb, f_bb))
} else {
None
}
} else {
None
}
}
}
Token::KwAdd
| Token::KwSub
| Token::KwMul
| Token::KwUDiv
| Token::KwSDiv
| Token::KwURem
| Token::KwSRem
| Token::KwFAdd
| Token::KwFSub
| Token::KwFMul
| Token::KwFDiv
| Token::KwFRem
| Token::KwShl
| Token::KwLShr
| Token::KwAShr
| Token::KwAnd
| Token::KwOr
| Token::KwXor => {
let _op = self.parse_binary_op()?;
let _ty = self.parse_type()?;
let a = self.parse_value()?;
self.expect(Token::Comma);
let b = self.parse_value()?;
let inst = instruction::add(a, b); inst.borrow_mut().name = result_name.unwrap_or("").to_string();
Some(inst)
}
Token::KwAlloca => {
self.advance();
let ty = self.parse_type()?;
if self.consume_if(&Token::Comma) {
self.parse_param_attributes();
}
let inst = instruction::alloca(ty);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
Token::KwLoad => {
self.advance();
let ty = self.parse_type()?;
self.expect(Token::Comma);
let _is_atomic = self.consume_if(&Token::KwAtomic);
let _ptr_ty = self.parse_type();
let ptr = self.parse_value()?;
self.parse_atomic_ordering();
while self.consume_if(&Token::Comma) {
self.parse_param_attributes();
let saved = self.pos;
if self.consume_if(&Token::Exclaim) {
self.pos = saved;
break;
}
}
let inst = instruction::load(ty, ptr);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
Token::KwStore => {
self.advance();
let _is_atomic = self.consume_if(&Token::KwAtomic);
let _ty = self.parse_type();
let val = self.parse_value()?;
self.expect(Token::Comma);
let _ptr_ty = self.parse_type();
let ptr = self.parse_value()?;
self.parse_atomic_ordering();
while self.consume_if(&Token::Comma) {
self.parse_param_attributes();
}
Some(instruction::store(val, ptr))
}
Token::KwICmp => {
self.advance();
let pred = self.parse_icmp_pred();
let _ty = self.parse_type();
let a = self.parse_value()?;
self.expect(Token::Comma);
let b = self.parse_value()?;
let inst = instruction::icmp(pred.unwrap_or(ICmpPred::Eq), a, b);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
Token::KwCall => self.parse_call_instruction(result_name),
Token::KwGetElementPtr => self.parse_gep_instruction(result_name),
Token::KwPhi => self.parse_phi_instruction(result_name),
Token::KwSelect => self.parse_select_instruction(result_name),
Token::KwUnreachable => {
self.advance();
let inst = instruction::unreachable();
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
Token::KwResume => {
self.advance();
let _ty = self.parse_type();
let val = self.parse_value()?;
let v = valref(
Value::new(Type::void())
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
v.borrow_mut().set_opcode(Opcode::Resume);
v.borrow_mut().push_operand(val);
Some(v)
}
Token::KwSwitch => self.parse_switch_instruction(result_name),
Token::KwInvoke => self.parse_invoke_instruction(result_name),
Token::KwLandingPad => self.parse_landingpad_instruction(result_name),
Token::KwFence => self.parse_fence_instruction(result_name),
Token::KwCmpXchg => self.parse_cmpxchg_instruction(result_name),
Token::KwAtomicRMW => self.parse_atomic_instruction(result_name),
Token::KwExtractValue => self.parse_extract_value(result_name),
Token::KwInsertValue => self.parse_insert_value(result_name),
Token::KwExtractElement => {
self.advance();
let _ty = self.parse_type();
let vec = self.parse_value()?;
self.expect(Token::Comma);
let _idx_ty = self.parse_type();
let idx = self.parse_value()?;
let inst = valref(
Value::new(Type::i32())
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::ExtractElement);
inst.borrow_mut().push_operand(vec);
inst.borrow_mut().push_operand(idx);
Some(inst)
}
Token::KwInsertElement => {
self.advance();
let _ty = self.parse_type();
let vec = self.parse_value()?;
self.expect(Token::Comma);
let _elem_ty = self.parse_type();
let elem = self.parse_value()?;
self.expect(Token::Comma);
let _idx_ty = self.parse_type();
let idx = self.parse_value()?;
let inst = valref(
Value::new(Type::i32())
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::InsertElement);
inst.borrow_mut().push_operand(vec);
inst.borrow_mut().push_operand(elem);
inst.borrow_mut().push_operand(idx);
Some(inst)
}
Token::KwShuffleVector => self.parse_shufflevector_instruction(result_name),
Token::KwTrunc
| Token::KwZExt
| Token::KwSExt
| Token::KwFPTrunc
| Token::KwFPExt
| Token::KwFPToUI
| Token::KwFPToSI
| Token::KwUIToFP
| Token::KwSIToFP
| Token::KwPtrToInt
| Token::KwIntToPtr
| Token::KwBitCast
| Token::KwAddrSpaceCast => {
let _op = self.advance();
let val = self.parse_value()?;
self.expect(Token::KwTo);
let to_ty = self.parse_type()?;
let inst = valref(
Value::new(to_ty)
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().push_operand(val);
Some(inst)
}
_ => {
self.advance();
None
}
}
}
fn parse_call_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let ret_ty = self.parse_type();
if let Some(Token::GlobalIdent(callee_name)) = self.advance() {
let cn = callee_name.clone();
self.expect(Token::LParen);
let mut args = Vec::new();
while self.peek() != Some(&Token::RParen) {
if let Some(_arg_ty) = self.parse_type() {
if let Some(arg_val) = self.parse_value() {
args.push(arg_val);
}
}
self.consume_if(&Token::Comma);
if self.peek() == Some(&Token::RParen) {
break;
}
}
self.expect(Token::RParen);
let callee = function::new_function(&cn, Type::void(), &[]);
let inst = instruction::call(ret_ty.unwrap_or(Type::void()), callee, args);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
} else {
None
}
}
fn parse_phi_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let ty = self.parse_type();
let mut incoming = Vec::new();
loop {
if self.peek() == Some(&Token::LBracket) {
self.advance();
let val = self.parse_value()?;
self.expect(Token::Comma);
if let Some(Token::LocalIdent(label)) = self.advance() {
let bb = crate::basic_block::new_basic_block(label);
incoming.push((val, bb));
}
self.expect(Token::RBracket);
} else {
break;
}
if !self.consume_if(&Token::Comma) {
break;
}
}
let inst = instruction::phi(ty.unwrap_or(Type::i32()), incoming);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
fn parse_select_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let ty = self.parse_type();
let cond = self.parse_value()?;
self.expect(Token::Comma);
let _ty_a = self.parse_type();
let a = self.parse_value()?;
self.expect(Token::Comma);
let _ty_b = self.parse_type();
let b = self.parse_value()?;
let inst = instruction::select(cond, a, b);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
fn parse_gep_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let _inbounds = self.consume_if(&Token::KwInBounds);
let _ty = self.parse_type();
self.expect(Token::Comma);
let _ptr_ty = self.parse_type();
let ptr = self.parse_value()?;
let mut indices = Vec::new();
while self.peek() != Some(&Token::LBrace)
&& self.peek() != Some(&Token::RBrace)
&& self.peek() != Some(&Token::Exclaim)
&& self.peek().is_some()
{
if self.consume_if(&Token::Comma) {
if let Some(_idx_ty) = self.parse_type() {
if let Some(idx_val) = self.parse_value() {
indices.push(idx_val);
}
}
} else {
break;
}
}
if indices.is_empty() {
let inst = instruction::getelementptr(Type::pointer(0), ptr, vec![]);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
} else {
let inst = instruction::getelementptr(Type::pointer(0), ptr, indices);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
}
fn parse_extract_value(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let agg_ty = self.parse_type()?;
let val = self.parse_value()?;
let mut indices = Vec::new();
while self.consume_if(&Token::Comma) {
if let Some(n) = self.parse_int_lit() {
indices.push(n as u32);
}
}
if indices.is_empty() {
indices.push(0);
}
let inst = valref(
Value::new(Type::i32())
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::ExtractValue);
inst.borrow_mut().push_operand(val);
let idx_str = indices
.iter()
.map(|i| i.to_string())
.collect::<Vec<_>>()
.join(",");
inst.borrow_mut().name = format!("{}.ev.{}", result_name.unwrap_or(""), idx_str);
Some(inst)
}
fn parse_insert_value(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let agg_ty = self.parse_type()?;
let agg = self.parse_value()?;
self.expect(Token::Comma);
let elem_ty = self.parse_type()?;
let elem = self.parse_value()?;
let mut indices = Vec::new();
while self.consume_if(&Token::Comma) {
if let Some(n) = self.parse_int_lit() {
indices.push(n as u32);
}
}
let inst = valref(
Value::new(agg_ty)
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::InsertValue);
inst.borrow_mut().push_operand(agg);
inst.borrow_mut().push_operand(elem);
Some(inst)
}
fn parse_atomic_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let _binop = self.advance(); let _ptr_ty = self.parse_type();
let ptr = self.parse_value()?;
self.expect(Token::Comma);
let _val_ty = self.parse_type();
let val = self.parse_value()?;
self.parse_atomic_ordering();
let inst = valref(
Value::new(Type::i32())
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::AtomicRMW);
inst.borrow_mut().push_operand(ptr);
inst.borrow_mut().push_operand(val);
Some(inst)
}
fn parse_fence_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); self.parse_atomic_ordering();
let inst = valref(
Value::new(Type::void())
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::Fence);
Some(inst)
}
fn parse_switch_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let _ty = self.parse_type();
let val = self.parse_value()?;
self.expect(Token::Comma);
self.expect(Token::KwLabel);
let default_label = self.advance()?;
let default_bb = if let Token::LocalIdent(l) = default_label {
crate::basic_block::new_basic_block(l)
} else {
return None;
};
let mut cases = Vec::new();
if self.consume_if(&Token::LBracket) {
loop {
if self.peek() == Some(&Token::RBracket) {
self.advance();
break;
}
let _case_ty = self.parse_type();
let case_val = self.parse_value()?;
self.expect(Token::Comma);
self.expect(Token::KwLabel);
if let Some(Token::LocalIdent(l)) = self.advance() {
let bb = crate::basic_block::new_basic_block(l);
cases.push((case_val, bb));
}
}
}
let inst = instruction::switch(val, default_bb, cases);
if let Some(name) = result_name {
inst.borrow_mut().name = name.to_string();
}
Some(inst)
}
fn parse_invoke_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let ret_ty = self.parse_type();
if let Some(Token::GlobalIdent(callee_name)) = self.advance() {
let cn = callee_name.clone();
self.expect(Token::LParen);
let mut args = Vec::new();
while self.peek() != Some(&Token::RParen) {
if let Some(_arg_ty) = self.parse_type() {
if let Some(arg_val) = self.parse_value() {
args.push(arg_val);
}
}
self.consume_if(&Token::Comma);
if self.peek() == Some(&Token::RParen) {
break;
}
}
self.expect(Token::RParen);
self.parse_fn_attributes();
self.expect(Token::KwTo);
self.expect(Token::KwLabel);
let normal_tok = self.advance()?;
self.expect(Token::LabelIdent("unwind".into()));
self.expect(Token::KwLabel);
let lpad_tok = self.advance()?;
let inst = valref(
Value::new(ret_ty.unwrap_or(Type::void()))
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::Invoke);
let callee = function::new_function(&cn, Type::void(), &[]);
inst.borrow_mut().push_operand(callee);
for a in args {
inst.borrow_mut().push_operand(a);
}
Some(inst)
} else {
None
}
}
fn parse_landingpad_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let result_ty = self.parse_type()?;
if self.consume_if(&Token::LabelIdent("cleanup".into())) {}
while let Some(Token::LabelIdent(s)) = self.peek() {
match s.as_str() {
"catch" => {
self.advance();
let _catch_ty = self.parse_type();
if let Some(Token::GlobalIdent(_)) = self.peek() {
self.advance();
} else {
self.parse_value();
}
}
"filter" => {
self.advance();
let _ = self.parse_value();
}
_ => break,
}
}
let inst = valref(
Value::new(result_ty)
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::LandingPad);
Some(inst)
}
fn parse_cmpxchg_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let _ptr_ty = self.parse_type();
let ptr = self.parse_value()?;
self.expect(Token::Comma);
let _old_ty = self.parse_type();
let old = self.parse_value()?;
self.expect(Token::Comma);
let _new_ty = self.parse_type();
let new = self.parse_value()?;
self.parse_atomic_ordering();
self.parse_atomic_ordering();
let inst = valref(
Value::new(Type::void())
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::CmpXchg);
inst.borrow_mut().push_operand(ptr);
inst.borrow_mut().push_operand(old);
inst.borrow_mut().push_operand(new);
Some(inst)
}
fn parse_shufflevector_instruction(&mut self, result_name: Option<&str>) -> Option<ValueRef> {
self.advance(); let _ty_a = self.parse_type();
let a = self.parse_value()?;
self.expect(Token::Comma);
let _ty_b = self.parse_type();
let b = self.parse_value()?;
self.expect(Token::Comma);
let _mask_ty = self.parse_type();
let _mask_val = self.parse_value();
let inst = valref(
Value::new(Type::fixed_vector_with(4, Type::i32().id))
.with_subclass(SubclassKind::Instruction)
.named(result_name.unwrap_or("")),
);
inst.borrow_mut().set_opcode(Opcode::ShuffleVector);
inst.borrow_mut().push_operand(a);
inst.borrow_mut().push_operand(b);
Some(inst)
}
fn parse_fn_attributes(&mut self) {
loop {
match self.peek() {
Some(Token::KwNoUnwind)
| Some(Token::KwReadOnly)
| Some(Token::KwReadNone)
| Some(Token::KwWriteOnly)
| Some(Token::KwNoInline)
| Some(Token::KwAlwaysInline)
| Some(Token::KwNoReturn)
| Some(Token::KwArgMemOnly)
| Some(Token::KwInaccessibleMemOnly)
| Some(Token::KwInaccessibleMemOrArgMemOnly)
| Some(Token::KwDsoLocal)
| Some(Token::KwDsoPreemptable)
| Some(Token::KwThreadLocal)
| Some(Token::KwUnnamedAddr)
| Some(Token::KwNoSanitize)
| Some(Token::KwOptNone)
| Some(Token::KwOptForFuzzing)
| Some(Token::KwShadowCallStack)
| Some(Token::KwSpeculativeLoadHardening)
| Some(Token::KwNoCfCheck)
| Some(Token::KwSanitizeAddress)
| Some(Token::KwSanitizeThread)
| Some(Token::KwSanitizeMemory)
| Some(Token::KwSanitizeHwaddress) => {
self.advance();
}
Some(Token::StringLit(_)) => {
self.advance();
if self.consume_if(&Token::Equal) {
if let Some(Token::StringLit(_)) = self.peek() {
self.advance();
}
}
}
_ => break,
}
}
}
fn parse_param_attributes(&mut self) {
loop {
match self.peek() {
Some(Token::KwImmArg) => {
self.advance();
}
Some(Token::LabelIdent(s)) => {
match s.as_str() {
"zeroext" | "signext" | "noalias" | "nocapture" | "byval" | "inreg"
| "nest" | "readonly" | "writeonly" | "swiftself" | "swifterror"
| "immarg" | "nofree" | "nosync" => {
self.advance();
}
"align" => {
self.advance();
let _ = self.parse_int_lit();
}
"dereferenceable" | "dereferenceable_or_null" => {
self.advance();
if self.consume_if(&Token::LParen) {
let _ = self.parse_int_lit();
self.expect(Token::RParen);
}
}
"byval" => {
self.advance();
if self.consume_if(&Token::LParen) {
let _ = self.parse_type();
self.expect(Token::RParen);
}
}
"preallocated" => {
self.advance();
if self.consume_if(&Token::LParen) {
let _ = self.parse_type();
self.expect(Token::RParen);
}
}
_ => break,
}
}
_ => break,
}
}
}
fn parse_attr_group(&mut self) {
if self.peek() == Some(&Token::AttrGroupId(0)) {
self.advance();
} else if let Some(Token::AttrGroupId(_)) = self.peek().cloned() {
self.advance();
}
self.expect(Token::Equal);
self.expect(Token::LBrace);
loop {
match self.peek() {
Some(Token::RBrace) => {
self.advance();
break;
}
Some(Token::StringLit(_)) => {
self.advance();
if self.consume_if(&Token::Equal) {
if let Some(Token::StringLit(_)) = self.peek() {
self.advance();
}
}
}
None => break,
_ => {
self.advance();
}
}
}
}
fn parse_return_attributes(&mut self) {
loop {
match self.peek() {
Some(Token::LabelIdent(s)) => match s.as_str() {
"noalias" | "nonnull" | "signext" | "zeroext" => {
self.advance();
}
"align" => {
self.advance();
let _ = self.parse_int_lit();
}
"dereferenceable" => {
self.advance();
if self.consume_if(&Token::LParen) {
let _ = self.parse_int_lit();
self.expect(Token::RParen);
}
}
_ => break,
},
_ => break,
}
}
}
fn parse_atomic_ordering(&mut self) {
match self.peek() {
Some(Token::KwAcquire)
| Some(Token::KwRelease)
| Some(Token::KwAcqRel)
| Some(Token::KwSeqCst)
| Some(Token::KwMonotonic) => {
self.advance();
}
_ => {}
}
}
fn parse_metadata_node(&mut self) {
if self.consume_if(&Token::Exclaim) {
if self.consume_if(&Token::LBrace) {
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.advance().unwrap() {
Token::LBrace => depth += 1,
Token::RBrace => depth -= 1,
_ => {}
}
}
}
}
}
fn parse_named_metadata(&mut self) {
match self.peek() {
Some(Token::MetadataName(_)) => {
self.advance(); self.expect(Token::Equal);
if self.consume_if(&Token::Exclaim) {
if self.consume_if(&Token::LBrace) {
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.advance().unwrap() {
Token::LBrace => depth += 1,
Token::RBrace => depth -= 1,
_ => {}
}
}
} else {
if self.peek().map(|t| matches!(t, Token::MetadataId(_))) == Some(true) {
self.advance();
}
}
}
}
_ => {}
}
}
fn parse_metadata_attachment(&mut self) {
if self.consume_if(&Token::Comma) {
if self.consume_if(&Token::Exclaim) {
if let Some(name) = self.peek().cloned() {
match name {
Token::LabelIdent(_) | Token::MetadataName(_) => {
self.advance(); if self.peek() == Some(&Token::LParen) {
let mut depth = 1;
self.advance(); while depth > 0 && self.peek().is_some() {
match self.advance().unwrap() {
Token::LParen => depth += 1,
Token::RParen => depth -= 1,
_ => {}
}
}
} else if let Some(Token::MetadataId(_))
| Some(Token::MetadataName(_)) = self.peek()
{
self.advance(); }
}
_ => {}
}
}
}
}
}
fn parse_debug_location(&mut self) {
if self.consume_if(&Token::Exclaim) {
if let Some(name) = self.peek().cloned() {
match name {
Token::LabelIdent(_) | Token::MetadataName(_) => {
self.advance();
if self.consume_if(&Token::LParen) {
let mut depth = 1;
while depth > 0 && self.peek().is_some() {
match self.advance().unwrap() {
Token::LParen => depth += 1,
Token::RParen => depth -= 1,
_ => {}
}
}
}
}
_ => {}
}
}
}
}
fn parse_comdat(&mut self) {
self.expect(Token::Equal);
self.consume_if(&Token::KwComdat); match self.peek() {
Some(Token::LabelIdent(s)) => {
let _kind = s.clone();
self.advance();
}
_ => {
self.advance(); }
}
}
fn parse_int_lit(&mut self) -> Option<i64> {
match self.peek()? {
Token::IntLit(v) => {
let v = *v;
self.advance();
Some(v)
}
_ => None,
}
}
fn parse_value(&mut self) -> Option<ValueRef> {
match self.peek()? {
Token::IntLit(v) => {
let v = *v;
self.advance();
Some(constants::const_i32(v as i32))
}
Token::GlobalIdent(name) => {
let name = name.clone();
self.advance();
if let Some(r) = self.globals.get(&name) {
Some(r.clone())
} else {
let f = function::new_function(&name, Type::void(), &[]);
Some(f)
}
}
Token::LocalIdent(name) => {
let name = name.clone();
self.advance();
if let Some(r) = self.locals.get(&name) {
Some(r.clone())
} else {
let v = valref(Value::new(Type::i32()).named(&name));
Some(v)
}
}
Token::KwNull => {
self.advance();
Some(constants::const_null_ptr(Type::pointer(0)))
}
Token::KwUndef => {
self.advance();
Some(constants::undef_value(Type::i32()))
}
Token::KwPoison => {
self.advance();
Some(constants::poison_value(Type::i32()))
}
Token::KwZeroinitializer => {
self.advance();
Some(constants::const_zero(Type::i32()))
}
Token::KwTrue => {
self.advance();
Some(constants::const_bool(true))
}
Token::KwFalse => {
self.advance();
Some(constants::const_bool(false))
}
Token::FloatLit(_v) => {
let _v = *_v;
self.advance();
Some(constants::const_float(_v))
}
_ => None,
}
}
fn parse_binary_op(&mut self) -> Option<Opcode> {
match self.advance()? {
Token::KwAdd => Some(Opcode::Add),
Token::KwSub => Some(Opcode::Sub),
Token::KwMul => Some(Opcode::Mul),
Token::KwUDiv => Some(Opcode::UDiv),
Token::KwSDiv => Some(Opcode::SDiv),
Token::KwURem => Some(Opcode::URem),
Token::KwSRem => Some(Opcode::SRem),
Token::KwFAdd => Some(Opcode::FAdd),
Token::KwFSub => Some(Opcode::FSub),
Token::KwFMul => Some(Opcode::FMul),
Token::KwFDiv => Some(Opcode::FDiv),
Token::KwFRem => Some(Opcode::FRem),
Token::KwShl => Some(Opcode::Shl),
Token::KwLShr => Some(Opcode::LShr),
Token::KwAShr => Some(Opcode::AShr),
Token::KwAnd => Some(Opcode::And),
Token::KwOr => Some(Opcode::Or),
Token::KwXor => Some(Opcode::Xor),
_ => None,
}
}
fn parse_icmp_pred(&mut self) -> Option<ICmpPred> {
match self.peek()? {
Token::LabelIdent(s) => {
let pred = match s.as_str() {
"eq" => ICmpPred::Eq,
"ne" => ICmpPred::Ne,
"ugt" => ICmpPred::Ugt,
"uge" => ICmpPred::Uge,
"ult" => ICmpPred::Ult,
"ule" => ICmpPred::Ule,
"sgt" => ICmpPred::Sgt,
"sge" => ICmpPred::Sge,
"slt" => ICmpPred::Slt,
"sle" => ICmpPred::Sle,
_ => return None,
};
self.advance();
Some(pred)
}
_ => None,
}
}
fn parse_global_variable(&mut self) {
if let Some(Token::GlobalIdent(name)) = self.advance() {
let saved_name = name.clone();
self.expect(Token::Equal);
if let Some(Token::LabelIdent(_)) = self.peek() {
self.advance(); }
let is_constant = self.consume_if(&Token::KwConstant);
if !is_constant {
self.consume_if(&Token::KwGlobal);
}
loop {
match self.peek() {
Some(Token::KwDsoLocal)
| Some(Token::KwDsoPreemptable)
| Some(Token::KwUnnamedAddr)
| Some(Token::KwThreadLocal)
| Some(Token::KwExternallyInitialized) => {
self.advance();
}
_ => break,
}
}
if let Some(_ty) = self.parse_type() {
if let Some(init) = self.parse_value() {
while self.consume_if(&Token::Comma) {
match self.peek() {
Some(Token::KwSection) => {
self.advance();
if let Some(Token::StringLit(_s)) = self.peek() {
self.advance();
}
}
Some(Token::KwPartition) => {
self.advance();
if let Some(Token::StringLit(_s)) = self.peek() {
self.advance();
}
}
Some(Token::KwComdat) | Some(Token::LabelIdent(_)) => {
self.advance();
}
_ => {
if let Some(Token::LabelIdent(s)) = self.peek() {
if s == "align" {
self.advance();
let _ = self.parse_int_lit();
} else {
self.advance();
}
} else {
self.advance();
}
}
}
}
let g = new_global(
Type::i32(),
is_constant,
crate::function::Linkage::External,
Some(init),
&saved_name,
);
self.globals.insert(saved_name, g.clone());
}
}
}
}
fn skip_attributes(&mut self) {
while let Some(t) = self.peek() {
match t {
Token::RBrace => {
self.advance();
break;
}
_ => {
self.advance();
}
}
}
}
fn skip_metadata(&mut self) {
while let Some(t) = self.peek() {
match t {
Token::RBrace => {
self.advance();
break;
}
_ => {
self.advance();
}
}
}
}
}
use constants::new_global;
pub fn parse_assembly(text: &str) -> Option<Module> {
let mut lexer = Lexer::new(text);
let tokens = lexer.tokenize();
let mut parser = Parser::new(&tokens);
let module = parser.parse_module();
if !parser.errors.is_empty() {
eprintln!("Parse errors ({}) :", parser.errors.len());
for e in &parser.errors {
eprintln!(" {}", e);
}
}
Some(module)
}
pub use parse_assembly as parse;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lexer_tokens() {
let mut lexer = Lexer::new("target triple = \"x86_64\"\ndeclare void @foo()\n");
let tokens = lexer.tokenize();
assert!(tokens.len() > 0);
let has_target = tokens.iter().any(|t| matches!(t, Token::KwTarget));
assert!(has_target);
}
#[test]
fn test_parse_empty_module() {
let text = "target triple = \"x86_64-unknown-linux-gnu\"\n";
let module = parse_assembly(text).unwrap();
assert_eq!(
module.target_triple,
Some("x86_64-unknown-linux-gnu".into())
);
}
#[test]
fn test_parse_declaration() {
let text = "target triple = \"x86_64-unknown-linux-gnu\"\ndeclare void @foo()\n";
let module = parse_assembly(text).unwrap();
assert_eq!(module.functions.len(), 1);
assert_eq!(module.functions[0].borrow().name, "foo");
}
#[test]
fn test_parse_simple_function() {
let text = r#"target triple = "x86_64-unknown-linux-gnu"
define i32 @main() {
entry:
ret i32 0
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
let main_func = module.functions.iter().find(|f| f.borrow().name == "main");
assert!(main_func.is_some());
}
#[test]
fn test_parse_function_with_instructions() {
let text = r#"target triple = "x86_64"
define i32 @add(i32 %a, i32 %b) {
entry:
%sum = add i32 %a, %b
ret i32 %sum
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_multiple_functions() {
let text = r#"
declare void @helper()
define i32 @main() {
entry:
call void @helper()
ret i32 0
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 2);
}
#[test]
fn test_parse_br_conditional() {
let text = r#"
define void @test() {
entry:
br i1 true, label %then, label %else
then:
ret void
else:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_alloca_and_store() {
let text = r#"
define void @test() {
entry:
%ptr = alloca i32
store i32 42, ptr %ptr
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_icmp() {
let text = r#"
define i1 @test(i32 %a, i32 %b) {
entry:
%cmp = icmp eq i32 %a, %b
ret i1 %cmp
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_roundtrip_simple() {
let text = r#"target triple = "x86_64-unknown-linux-gnu"
declare void @foo()
"#;
let module = parse_assembly(text).unwrap();
let output = crate::asm_writer::write_assembly(&module);
assert!(output.contains("x86_64"));
assert!(output.contains("@foo"));
}
#[test]
fn test_parse_source_filename() {
let text = r#"source_filename = "test.c"
target triple = "x86_64"
"#;
let module = parse_assembly(text).unwrap();
assert_eq!(module.source_filename, "test.c");
}
#[test]
fn test_parse_data_layout() {
let text = r#"target datalayout = "e-m:e-p270:32:32"
"#;
let module = parse_assembly(text).unwrap();
assert_eq!(module.data_layout, Some("e-m:e-p270:32:32".into()));
}
#[test]
fn test_lexer_global_ident() {
let mut lexer = Lexer::new("@main @foo @bar_baz.42");
let tokens = lexer.tokenize();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens[0], Token::GlobalIdent("main".into()));
assert_eq!(tokens[2], Token::GlobalIdent("bar_baz.42".into()));
}
#[test]
fn test_lexer_local_ident() {
let mut lexer = Lexer::new("%result %tmp.1 %2");
let tokens = lexer.tokenize();
assert_eq!(tokens.len(), 3);
assert_eq!(tokens[0], Token::LocalIdent("result".into()));
}
#[test]
fn test_lexer_numbers() {
let mut lexer = Lexer::new("42 -1 3.14");
let tokens = lexer.tokenize();
assert!(tokens.iter().any(|t| matches!(t, Token::IntLit(42))));
}
#[test]
fn test_parse_phi_instruction() {
let text = r#"
define i32 @test(i1 %cond) {
entry:
br i1 %cond, label %a, label %b
a:
br label %merge
b:
br label %merge
merge:
%x = phi i32 [ 1, %a ], [ 2, %b ]
ret i32 %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_gep_instruction() {
let text = r#"
define ptr @test(ptr %p, i32 %i) {
entry:
%x = getelementptr i32, ptr %p, i32 %i
ret ptr %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_struct_type() {
let text = r#"
%struct.Foo = type { i32, i8, double }
define void @test() {
entry:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_packed_struct_type() {
let text = r#"
%struct.Packed = type <{ i32, i8 }>
define void @test() {
entry:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_vector_type() {
let text = r#"
define <4 x i32> @test() {
entry:
ret <4 x i32> zeroinitializer
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_scalable_vector_type() {
let text = r#"
define <vscale x 4 x float> @test() {
entry:
ret <vscale x 4 x float> zeroinitializer
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_array_type() {
let text = r#"
define [10 x i32] @test() {
entry:
ret [10 x i32] zeroinitializer
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_select_instruction() {
let text = r#"
define i32 @test(i1 %cond, i32 %a, i32 %b) {
entry:
%x = select i1 %cond, i32 %a, i32 %b
ret i32 %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_unreachable() {
let text = r#"
define void @test() {
entry:
unreachable
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_switch_instruction() {
let text = r#"
define void @test(i32 %val) {
entry:
switch i32 %val, label %default [
i32 1, label %a
i32 2, label %b
]
default:
ret void
a:
ret void
b:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_landingpad() {
let text = r#"
@_ZTIi = external global i8
define void @test() personality ptr @__gxx_personality_v0 {
entry:
ret void
lpad:
%exn = landingpad { ptr, i32 } cleanup catch ptr @_ZTIi
ret void
}
declare i32 @__gxx_personality_v0(...)
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 2); }
#[test]
fn test_parse_fence_instruction() {
let text = r#"
define void @test() {
entry:
fence acquire
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_atomicrmw() {
let text = r#"
define i32 @test(ptr %p, i32 %v) {
entry:
%old = atomicrmw add ptr %p, i32 %v seq_cst
ret i32 %old
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_cmpxchg() {
let text = r#"
define { i32, i1 } @test(ptr %p, i32 %old, i32 %new) {
entry:
%x = cmpxchg ptr %p, i32 %old, i32 %new seq_cst monotonic
ret { i32, i1 } %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_function_with_attributes() {
let text = r#"
define void @test() nounwind readonly ssp {
entry:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_attribute_group() {
let text = r#"
attributes #0 = { nounwind ssp "target-cpu"="x86-64" }
define void @test() {
entry:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_named_metadata() {
let text = r#"
!llvm.module.flags = !{ !0, !1 }
!0 = !{ i32 1, !"wchar_size", i32 4 }
define void @test() {
entry:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_shufflevector() {
let text = r#"
define <4 x i32> @test(<4 x i32> %a, <4 x i32> %b) {
entry:
%x = shufflevector <4 x i32> %a, <4 x i32> %b,
<4 x i32> <i32 0, i32 1, i32 2, i32 3>
ret <4 x i32> %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_extractvalue() {
let text = r#"
%struct.Pair = type { i32, float }
define i32 @test(%struct.Pair %s) {
entry:
%x = extractvalue %struct.Pair %s, 0
ret i32 %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_insertvalue() {
let text = r#"
%struct.Pair = type { i32, float }
define %struct.Pair @test(%struct.Pair %s, i32 %v) {
entry:
%x = insertvalue %struct.Pair %s, i32 %v, 0
ret %struct.Pair %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_pointer_addrspace() {
let text = r#"
define ptr addrspace(5) @test(ptr addrspace(5) %p) {
entry:
ret ptr addrspace(5) %p
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_gep_inbounds() {
let text = r#"
define ptr @test(ptr %p, i32 %i) {
entry:
%x = getelementptr inbounds i32, ptr %p, i32 %i
ret ptr %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_cast_operators() {
let text = r#"
define i32 @test(ptr %p) {
entry:
%x = ptrtoint ptr %p to i32
ret i32 %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_invoke_instruction() {
let text = r#"
declare i32 @foo()
define void @test() personality ptr @__gxx_personality_v0 {
entry:
%x = invoke i32 @foo()
to label %normal unwind label %lpad
normal:
ret void
lpad:
%exn = landingpad { ptr, i32 } cleanup
ret void
}
declare i32 @__gxx_personality_v0(...)
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 3);
}
#[test]
fn test_parse_extractelement() {
let text = r#"
define i32 @test(<4 x i32> %v, i32 %i) {
entry:
%x = extractelement <4 x i32> %v, i32 %i
ret i32 %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_insertelement() {
let text = r#"
define <4 x i32> @test(<4 x i32> %v, i32 %e, i32 %i) {
entry:
%x = insertelement <4 x i32> %v, i32 %e, i32 %i
ret <4 x i32> %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_resume_instruction() {
let text = r#"
define void @test({ ptr, i32 } %exn) {
entry:
resume { ptr, i32 } %exn
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_lexer_extended_keywords() {
let mut lexer =
Lexer::new("inbounds nuw nsw exact acquire release acq_rel seq_cst monotonic");
let tokens = lexer.tokenize();
assert!(tokens.iter().any(|t| matches!(t, Token::KwInBounds)));
assert!(tokens.iter().any(|t| matches!(t, Token::KwNuw)));
assert!(tokens.iter().any(|t| matches!(t, Token::KwAcqRel)));
assert!(tokens.iter().any(|t| matches!(t, Token::KwSeqCst)));
}
#[test]
fn test_parse_global_variable_with_attributes() {
let text = r#"
@var = global i32 0, align 4
define void @test() {
entry:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_function_ptr_type() {
let text = r#"
declare void @foo()
define void @test(ptr %fn) {
entry:
%x = call void @foo()
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 2);
}
#[test]
fn test_parse_comdat() {
let text = r#"
$foo = comdat any
define void @test() comdat {
entry:
ret void
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
#[test]
fn test_parse_load_atomic() {
let text = r#"
define i32 @test(ptr %p) {
entry:
%x = load atomic i32, ptr %p seq_cst, align 4
ret i32 %x
}
"#;
let module = parse_assembly(text).unwrap();
assert!(module.functions.len() >= 1);
}
}