diff --git a/filetests/parser/call.cton b/filetests/parser/call.cton index 71c7c1efb0..72a61b6e21 100644 --- a/filetests/parser/call.cton +++ b/filetests/parser/call.cton @@ -22,3 +22,49 @@ ebb1: ; nextln: v1 = f32const 0.0 ; nextln: return v0, v1 ; nextln: } + +function signatures() { + sig10 = signature() + sig11 = signature(i32, f64) -> i32, b1 + fn5 = sig11 foo + fn8 = function bar(i32) -> b1 +} +; sameln: function signatures() { +; nextln: $sig10 = signature() +; nextln: $sig11 = signature(i32, f64) -> i32, b1 +; nextln: sig2 = signature(i32) -> b1 +; nextln: $fn5 = $sig11 foo +; nextln: $fn8 = sig2 bar +; nextln: } + +function direct() { + fn0 = function none() + fn1 = function one() -> i32 + fn2 = function two() -> i32, f32 + +ebb0: + call fn0() + v1 = call fn1() + v2, v3 = call fn2() + return +} +; check: call $fn0() +; check: $v1 = call $fn1() +; check: $v2, $v3 = call $fn2() +; check: return + +function indirect(i64) { + sig0 = signature(i64) + sig1 = signature() -> i32 + sig2 = signature() -> i32, f32 + +ebb0(v0: i64): + v1 = call_indirect sig1, v0() + call_indirect sig0, v1(v0) + v3, v4 = call_indirect sig2, v1() + return +} +; check: $v1 = call_indirect $sig1, $v0() +; check: call_indirect $sig0, $v1($v0) +; check: $v3, $v4 = call_indirect $sig2, $v1() +; check: return diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 186fd6df1e..7b16abe272 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -9,12 +9,14 @@ use std::str::FromStr; use std::u32; use std::mem; use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable, - JumpTableData, Signature, ArgumentType, ArgumentExtension}; + JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef, + FuncRef}; use cretonne::ir::types::VOID; use cretonne::ir::immediates::{Imm64, Ieee32, Ieee64}; use cretonne::ir::entities::{AnyEntity, NO_EBB, NO_INST, NO_VALUE}; use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs, - TernaryOverflowData, JumpData, BranchData, ReturnData}; + TernaryOverflowData, JumpData, BranchData, CallData, + IndirectCallData, ReturnData}; use cretonne::isa; use cretonne::settings; use testfile::{TestFile, Details, Comment}; @@ -84,6 +86,32 @@ impl Context { self.map.def_ss(number, self.function.stack_slots.push(data), loc) } + // Allocate a new signature and add a mapping number -> SigRef. + fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> { + self.map.def_sig(number, self.function.dfg.signatures.push(data), loc) + } + + // Resolve a reference to a signature. + fn get_sig(&self, number: u32, loc: &Location) -> Result { + match self.map.get_sig(number) { + Some(sig) => Ok(sig), + None => err!(loc, "undefined signature sig{}", number), + } + } + + // Allocate a new external function and add a mapping number -> FuncRef. + fn add_fn(&mut self, number: u32, data: ExtFuncData, loc: &Location) -> Result<()> { + self.map.def_fn(number, self.function.dfg.ext_funcs.push(data), loc) + } + + // Resolve a reference to a function. + fn get_fn(&self, number: u32, loc: &Location) -> Result { + match self.map.get_fn(number) { + Some(fnref) => Ok(fnref), + None => err!(loc, "undefined function fn{}", number), + } + } + // Allocate a new jump table and add a mapping number -> JumpTable. fn add_jt(&mut self, number: u32, data: JumpTableData, loc: &Location) -> Result<()> { self.map.def_jt(number, self.function.jump_tables.push(data), loc) @@ -305,6 +333,26 @@ impl<'a> Parser<'a> { } } + // Match and consume a function reference. + fn match_fn(&mut self, err_msg: &str) -> Result { + if let Some(Token::FuncRef(fnref)) = self.token() { + self.consume(); + Ok(fnref) + } else { + err!(self.loc, err_msg) + } + } + + // Match and consume a signature reference. + fn match_sig(&mut self, err_msg: &str) -> Result { + if let Some(Token::SigRef(sigref)) = self.token() { + self.consume(); + Ok(sigref) + } else { + err!(self.loc, err_msg) + } + } + // Match and consume a jump table reference. fn match_jt(&mut self) -> Result { if let Some(Token::JumpTable(jt)) = self.token() { @@ -628,6 +676,16 @@ impl<'a> Parser<'a> { self.parse_stack_slot_decl() .and_then(|(num, dat)| ctx.add_ss(num, dat, &self.loc)) } + Some(Token::SigRef(..)) => { + self.gather_comments(ctx.function.dfg.signatures.next_key()); + self.parse_signature_decl() + .and_then(|(num, dat)| ctx.add_sig(num, dat, &self.loc)) + } + Some(Token::FuncRef(..)) => { + self.gather_comments(ctx.function.dfg.ext_funcs.next_key()); + self.parse_function_decl(ctx) + .and_then(|(num, dat)| ctx.add_fn(num, dat, &self.loc)) + } Some(Token::JumpTable(..)) => { self.gather_comments(ctx.function.jump_tables.next_key()); self.parse_jump_table_decl() @@ -661,6 +719,56 @@ impl<'a> Parser<'a> { Ok((number, data)) } + // Parse a signature decl. + // + // signature-decl ::= SigRef(sigref) "=" "signature" signature + // + fn parse_signature_decl(&mut self) -> Result<(u32, Signature)> { + let number = try!(self.match_sig("expected signature number: sig«n»")); + try!(self.match_token(Token::Equal, "expected '=' in signature decl")); + try!(self.match_identifier("signature", "expected 'signature'")); + let data = try!(self.parse_signature()); + Ok((number, data)) + } + + // Parse a function decl. + // + // Two variants: + // + // function-decl ::= FuncRef(fnref) "=" function-spec + // FuncRef(fnref) "=" SigRef(sig) name + // + // The first variant allocates a new signature reference. The second references an existing + // signature which must be declared first. + // + fn parse_function_decl(&mut self, ctx: &mut Context) -> Result<(u32, ExtFuncData)> { + let number = try!(self.match_fn("expected function number: fn«n»")); + try!(self.match_token(Token::Equal, "expected '=' in function decl")); + + let data = match self.token() { + Some(Token::Identifier("function")) => { + let (loc, name, sig) = try!(self.parse_function_spec()); + let sigref = ctx.function.dfg.signatures.push(sig); + ctx.map.def_entity(sigref.into(), &loc).expect("duplicate SigRef entities created"); + ExtFuncData { + name: name, + signature: sigref, + } + } + Some(Token::SigRef(sig_src)) => { + let sig = try!(ctx.get_sig(sig_src, &self.loc)); + self.consume(); + let name = try!(self.parse_function_name()); + ExtFuncData { + name: name, + signature: sig, + } + } + _ => return err!(self.loc, "expected 'function' or sig«n» in function decl"), + }; + Ok((number, data)) + } + // Parse a jump table decl. // // jump-table-decl ::= * JumpTable(jt) "=" "jump_table" jt-entry {"," jt-entry} @@ -1177,10 +1285,39 @@ impl<'a> Parser<'a> { } } InstructionFormat::Call => { - unimplemented!(); + let func_ref = try!(self.match_fn("expected function reference") + .and_then(|num| ctx.get_fn(num, &self.loc))); + try!(self.match_token(Token::LPar, "expected '(' before arguments")); + let args = try!(self.parse_value_list()); + try!(self.match_token(Token::RPar, "expected ')' after arguments")); + InstructionData::Call { + opcode: opcode, + ty: VOID, + second_result: NO_VALUE, + data: Box::new(CallData { + func_ref: func_ref, + varargs: args, + }), + } } InstructionFormat::IndirectCall => { - unimplemented!(); + let sig_ref = try!(self.match_sig("expected signature reference") + .and_then(|num| ctx.get_sig(num, &self.loc))); + try!(self.match_token(Token::Comma, "expected ',' between operands")); + let callee = try!(self.match_value("expected SSA value callee operand")); + try!(self.match_token(Token::LPar, "expected '(' before arguments")); + let args = try!(self.parse_value_list()); + try!(self.match_token(Token::RPar, "expected ')' after arguments")); + InstructionData::IndirectCall { + opcode: opcode, + ty: VOID, + second_result: NO_VALUE, + data: Box::new(IndirectCallData { + sig_ref: sig_ref, + arg: callee, + varargs: args, + }), + } } InstructionFormat::Return => { let args = try!(self.parse_value_list());