Commit d27813e2 authored by Isaac Oscar Gariano's avatar Isaac Oscar Gariano

Implemented tailcall on aarch64

parent caf2aa5a
Pipeline #730 passed with stages
in 28 minutes and 38 seconds
......@@ -307,6 +307,8 @@ impl MuFunctionVersion {
ret
}
// TODO: It may be more efficient to compute this when the instructions
// are added to the function version and store the result in a field
pub fn has_throw(&self) -> bool {
let f_content = self.content.as_ref().unwrap();
......@@ -332,6 +334,32 @@ impl MuFunctionVersion {
false
}
pub fn has_tailcall(&self) -> bool {
let f_content = self.content.as_ref().unwrap();
for (_, block) in f_content.blocks.iter() {
let block_content = block.content.as_ref().unwrap();
for inst in block_content.body.iter() {
match inst.v {
TreeNode_::Instruction(ref inst) => {
match inst.v {
Instruction_::TailCall(_) => {return true;}
_ => {
// do nothing
}
}
},
_ => {
unreachable!()
}
}
}
}
false
}
}
/// FunctionContent contains all blocks (which include all instructions) for the function
......
......@@ -1310,23 +1310,6 @@ impl ASMCodeGen {
)
}
fn internal_binop_str(&mut self, inst: &str, dest: &P<Value>, src1: &P<Value>, src2: &str) {
let inst = inst.to_string();
trace!("emit: \t{} {}, {} -> {}", inst, src1, src2, dest);
let (reg1, id1, loc1) = self.prepare_reg(dest, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(src1, inst.len() + 1 + reg1.len() + 1);
let asm = format!("{} {},{},#{}", inst, reg1, reg2, src2);
self.add_asm_inst(
asm,
ignore_zero_register(id1, vec![loc1]),
ignore_zero_register(id2, vec![loc2]),
false
)
}
// dest <= inst(src1, src2)
fn internal_unop_shift(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>, shift: &str, amount: u8) {
let inst = inst.to_string();
......@@ -1717,7 +1700,7 @@ impl ASMCodeGen {
// TODO: What to do when src1/src2/stack are the same???
fn internal_load_pair(&mut self, inst: &str, dest1: &P<Value>, dest2: &P<Value>, src: &P<Value>) {
let inst = inst.to_string();
trace!("emit: \t{} {} -> {},{}", inst, src, dest1, dest2);
trace!("emit: \t{} {} -> {}, {}", inst, src, dest1, dest2);
let (reg1, id1, loc1) = self.prepare_reg(dest1, inst.len() + 1);
let (reg2, id2, loc2) = self.prepare_reg(dest2, inst.len() + 1 + reg1.len() + 1);
......@@ -2050,8 +2033,6 @@ impl CodeGenerator for ASMCodeGen {
)
}
fn emit_add_str(&mut self, dest: Reg, src1: Reg, src2: &str) {self.internal_binop_str("ADD", dest, src1, src2)}
// Pushes a pair of registers on the givne stack (uses the STP instruction)
fn emit_push_pair(&mut self, src1: &P<Value>, src2: &P<Value>, stack: &P<Value>) {
trace!("emit: \tpush_pair {},{} -> {}[-8,-16]", src1, src2, stack);
......
......@@ -47,10 +47,6 @@ pub trait CodeGenerator {
// emit code to adjust frame
fn emit_frame_grow(&mut self); // Emits a SUB
// Used to pass a string that the assembler will interpret as an immediate argument
// (This is neccesary to support the use of ELF relocations like ':tprel_hi12:foo')
fn emit_add_str(&mut self, dest: Reg, src1: Reg, src2: &str);
// stack minimpulation
fn emit_push_pair(&mut self, src1: Reg, src2: Reg, stack: Reg); // Emits a STP
fn emit_pop_pair(&mut self, dest1: Reg, dest2: Reg, stack: Reg); // Emits a LDP
......@@ -59,6 +55,8 @@ pub trait CodeGenerator {
fn emit_ldr_callee_saved(&mut self, dest: Reg, src: Mem);
fn emit_str_callee_saved(&mut self, dest: Mem, src: Reg);
//==================================================================================================
/* Bellow ar all ARMv8-A Aarch64 instruction menmonics (with all operand modes) except:
PRFM, PRFUM, CRC32*
All advanced SIMD instructions (except MOVI)
......@@ -123,8 +121,8 @@ pub trait CodeGenerator {
fn emit_mrs(&mut self, dest: Reg, src: &str);
// Address calculation
fn emit_adr(&mut self, dest: Reg, src: Reg);
fn emit_adrp(&mut self, dest: Reg, src: Reg);
fn emit_adr(&mut self, dest: Reg, src: Mem);
fn emit_adrp(&mut self, dest: Reg, src: Mem);
// Unary ops
fn emit_mov(&mut self, dest: Reg/*GPR or SP or ZR*/, src: Reg/*GPR or SP or ZR*/); // The SP and ZR cannot both be used
......
......@@ -396,7 +396,7 @@ lazy_static! {
//X18.clone(), // Platform Register
];
#[allow(dead_code)]
/*#[allow(dead_code)]
static ref ALL_GPRS : [P<Value>; 30] = [
X0.clone(),
X1.clone(),
......@@ -429,7 +429,7 @@ lazy_static! {
X28.clone(),
X29.clone(), // Frame Pointer
X30.clone() // Link Register
];
];*/
}
pub const FPR_ID_START : usize = 100;
......@@ -586,7 +586,7 @@ lazy_static!{
D31.clone()
];
#[allow(dead_code)]
/*#[allow(dead_code)]
static ref ALL_FPRS : [P<Value>; 32] = [
D0.clone(),
D1.clone(),
......@@ -622,7 +622,7 @@ lazy_static!{
D29.clone(),
D30.clone(),
D31.clone()
];
];*/
}
lazy_static! {
......@@ -2005,8 +2005,10 @@ fn emit_fpreg_value(backend: &mut CodeGenerator, pv: &P<Value>, f_context: &mut
}
fn split_int128(int128: &P<Value>, f_context: &mut FunctionContext, vm: &VM) -> (P<Value>, P<Value>) {
trace!("ISAAC split_int128({})...", int128);
if f_context.get_value(int128.id()).unwrap().has_split() {
let vec = f_context.get_value(int128.id()).unwrap().get_split().as_ref().unwrap();
trace!("ISAAC <- get value ({}, {})", &vec[0], &vec[1]);
(vec[0].clone(), vec[1].clone())
} else {
let arg_l = make_temporary(f_context, UINT64_TYPE.clone(), vm);
......@@ -2014,11 +2016,13 @@ fn split_int128(int128: &P<Value>, f_context: &mut FunctionContext, vm: &VM) ->
f_context.get_value_mut(int128.id()).unwrap().set_split(vec![arg_l.clone(), arg_h.clone()]);
trace!("ISAAC <- make temporary ({}, {})", &arg_l, &arg_h);
(arg_l, arg_h)
}
}
pub fn emit_ireg_ex_value(backend: &mut CodeGenerator, pv: &P<Value>, f_context: &mut FunctionContext, vm: &VM) -> (P<Value>, P<Value>) {
trace!("ISAAC emit_ireg_ex_value({})", pv);
match pv.v {
Value_::SSAVar(_) => {
split_int128(pv, f_context, vm)
......@@ -2032,6 +2036,7 @@ pub fn emit_ireg_ex_value(backend: &mut CodeGenerator, pv: &P<Value>, f_context:
emit_mov_u64(backend, &tmp_l, val[0]);
emit_mov_u64(backend, &tmp_h, val[1]);
trace!("ISAAC <- ({}, {}) = ({}, {})", &tmp_l, &tmp_h, val[0], val[1]);
(tmp_l, tmp_h)
},
_ => panic!("expected ireg_ex")
......
......@@ -125,12 +125,13 @@ impl Inlining {
let n_insts = estimate_insts(&fv);
let out_calls = fv.get_static_call_edges();
let has_throw = fv.has_throw();
let has_tailcall = fv.has_tailcall();
// simple heuristic here:
// * estimated machine insts are fewer than 10 insts
// * leaf in call graph (no out calls)
// * no throw (otherwise we will need to rearrange catch)
let should_inline = n_insts <= 25 && out_calls.len() == 0 && !has_throw;
let should_inline = n_insts <= 25 && out_calls.len() == 0 && !has_throw && !has_tailcall;
trace!("func {} has {} insts (estimated)", callee, n_insts);
trace!(" has {} out calls", out_calls.len());
......
......@@ -78,16 +78,16 @@ def test_add():
def test_except_stack_args():
compile_bundle(
"""
.funcsig stack_sig = (int<64> int<64> int<64> int<64> int<64> int<64> int<64>)->()
.funcsig stack_sig = (int<64> int<64> int<64> int<64> int<64> int<64> int<64> int<64> int<64>)->()
.funcdef stack_args <stack_sig>
{
entry(<int<64>> v0 <int<64>> v1 <int<64>> v2 <int<64>> v3 <int<64>> v4 <int<64>> v5 <int<64>> v6):
entry(<int<64>> v0 <int<64>> v1 <int<64>> v2 <int<64>> v3 <int<64>> v4 <int<64>> v5 <int<64>> v6 <int<64>> v7 <int<64>> v8):
THROW <ref<void>> NULL
}
.funcdef test_except_stack_args <main_sig>
{
entry(<int<32>>argc <uptr<uptr<char>>>argv):
CALL <stack_sig> stack_args(<int<32>>0 <int<32>>1 <int<32>>2 <int<32>>3 <int<32>>4 <int<32>>5 <int<32>>6)
CALL <stack_sig> stack_args(<int<32>>0 <int<32>>1 <int<32>>2 <int<32>>3 <int<32>>4 <int<32>>5 <int<32>>6 <int<32>>7 <int<32>>8)
EXC (exit(<int<32>> 0) exit(<int<32>> 1))
exit(<int<32>> status):
......@@ -95,4 +95,16 @@ def test_except_stack_args():
}
""",
"test_except_stack_args");
assert(execute("test_except_stack_args") == 1);
\ No newline at end of file
assert(execute("test_except_stack_args") == 1);
def test_ldp_bug():
compile_bundle(
"""
.funcdef foo <(int<128> int<128> int<128> int<128> int<128> int<128>)->(int<128>)>
{
entry(<int<128>>a0 <int<128>>a1 <int<128>>a2 <int<128>>a3 <int<128>>a4 <int<128>>a5):
RET a5
}
""", "test_taillcall_smaller_stack");
assert(execute("test_taillcall_smaller_stack") == 12);
\ No newline at end of file
# Copyright 2017 The Australian National University
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from util import execute, compile_bundle, load_bundle, get_function;
import pytest;
import ctypes;
def test_taillcall_simple():
compile_bundle(
"""
.funcdef test_taillcall_simple <main_sig>
{
entry(<int<32>>argc <uptr<uptr<char>>>argv):
TAILCALL <main_sig>taillcallee(argc argv)
}
.funcdef taillcallee <main_sig>
{
entry(<int<32>>argc <uptr<uptr<char>>>argv):
RET argc
}
""", "test_taillcall_simple");
assert(execute("test_taillcall_simple", ["2", "3", "4"]) == 4);
#TODO: WHy does returning a pair of int<128>'s fail?
def test_taillcall_smaller_stack():
compile_bundle(
"""
.funcsig big_sig = (int<128> int<128> int<128> int<128> int<128> int<128>)->(int<128>)
.funcsig small_sig = (int<128> int<128> int<128> int<128> int<128>) ->(int<128>)
.funcdef test_taillcall_smaller_stack <main_sig>
{
entry(<int<32>>argc <uptr<uptr<char>>>argv):
res_128 = CALL <big_sig> bigger_stack(<int<128>>0 <int<128>>1 <int<128>>2 <int<128>>3 <int<128>>4 <int<128>>5)
res = TRUNC <int<128> int<32>> res_128
RET res
}
.funcdef bigger_stack <big_sig>
{
entry(<int<128>>a0 <int<128>>a1 <int<128>>a2 <int<128>>a3 <int<128>>a4 <int<128>>a5):
TAILCALL <small_sig> smaller_stack(a0 a1 a2 a3 a4)
}
.funcdef smaller_stack <small_sig>
{
entry(<int<128>>a0 <int<128>>a1 <int<128>>a2 <int<128>>a3 <int<128>>a4):
res_01 = ADD<int<128>> a0 a1
res_013 = ADD<int<128>> res_01 a3
res_24 = MUL<int<128>> a2 a4
res_013_24 = ADD<int<128>> res_013 res_24
RET res_013_24
}
""", "test_taillcall_smaller_stack");
assert(execute("test_taillcall_smaller_stack") == 12);
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment