Commit 665eaf74 authored by Eduardo Souza's avatar Eduardo Souza

Added support for GC through MMTk.

parent dd81fdef
......@@ -19,9 +19,12 @@ pub use mu::compiler::*;
use std::collections::HashMap;
use mu::ast::ir::*;
use mu::compiler::backend::x86_64::callconv::CallConvResult::STACK;
use mu::utils::*;
use mu::vm::handle::*;
use mu::vm::*;
use std::path::Path;
use ykstackmaps::StackMapParser;
pub fn llvm(wl_fns: Vec<MuID>) -> CompilerPolicy {
let mut passes: Vec<Box<dyn CompilerPass>> = vec![];
......@@ -281,6 +284,8 @@ pub fn make_boot_image_internal(
primordial_threadlocal.map(|x| x.v.as_ref().1)
);
let out_file = output_file.clone();
// link
if is_test {
link_boot_image_for_testing(
......@@ -297,6 +302,118 @@ pub fn make_boot_image_internal(
true
);
}
// parse_stackmap(out_file, vm);
}
}
fn parse_stackmap(path: String, vm: &VM) {
use std::path::PathBuf;
use ykstackmaps::StackMapParser;
let mut file_path = PathBuf::from(&vm.vm_options.flag_aot_emit_dir);
let path = format!("{}", path);
file_path.push(path);
println!("{:?}", file_path);
match StackMapParser::new(&Path::new(&file_path)) {
Err(e) => {
println!("error: {}", e);
unimplemented!();
}
Ok(mut p) => {
p.num_consts();
p.num_funcs();
p.num_stackmaps();
println!(
"num consts: {}, num funcs: {}, num_stackmaps: {}",
p.num_consts(),
p.num_funcs(),
p.num_stackmaps()
);
for stmap_fns in p.iter_functions() {
match stmap_fns {
Ok(stmap) => println!("{:?}", stmap),
Err(e) => {
panic!("error: {}", e);
break; // You must not re-use the iterator upon error.
}
}
}
for stmap_res in p.iter_stackmaps() {
match stmap_res {
Ok(stmap) => {
println!("{:?}", stmap);
match stmap.pos_remainder {
Some(pos) => parse_stackmap_from_pos(
&Path::new(&file_path),
pos
),
None => ()
}
}
Err(e) => {
panic!("error: {}", e);
break; // You must not re-use the iterator upon error.
}
}
}
}
}
unimplemented!();
}
fn parse_stackmap_from_pos(path: &&Path, pos: u64) {
match StackMapParser::new_from_position(path, pos) {
Err(e) => {
println!("error: {}", e);
unimplemented!();
}
Ok(mut p) => {
p.num_consts();
p.num_funcs();
p.num_stackmaps();
println!(
"num consts: {}, num funcs: {}, num_stackmaps: {}",
p.num_consts(),
p.num_funcs(),
p.num_stackmaps()
);
for stmap_fns in p.iter_functions_from_pos(pos) {
match stmap_fns {
Ok(stmap) => println!("{:?}", stmap),
Err(e) => {
panic!("error: {}", e);
break; // You must not re-use the iterator upon error.
}
}
}
for stmap_res in p.iter_stackmaps_from_pos(pos) {
match stmap_res {
Ok(stmap) => {
println!("{:?}", stmap);
match stmap.pos_remainder {
Some(pos) => parse_stackmap_from_pos(path, pos),
None => ()
}
}
Err(e) => {
panic!("error: {}", e);
break; // You must not re-use the iterator upon error.
}
}
}
}
}
}
......
......@@ -1194,7 +1194,7 @@ pub unsafe fn gen_instr_exprcall(
(0, Arc::new(String::from("\0")))
} else {
let n = n.get(0).unwrap();
(n.hdr.id(), n.hdr.name())
(n.hdr.id(), Arc::new(format!("{}\0", n.hdr.name())))
}
}
None => unimplemented!()
......@@ -1406,7 +1406,8 @@ pub unsafe fn gen_instr_new(
llvm_internal_context: &mut AOTLLVMInternalContext,
store: &mut HashMap<MuID, LLVMValueRef>,
ins: &Instruction,
llvm_type: LLVMTypeRef
llvm_type: LLVMTypeRef,
mu_type: MuID
) {
let ins_value = ins.value.clone().unwrap();
if ins_value.len() > 1 {
......@@ -1417,22 +1418,30 @@ pub unsafe fn gen_instr_new(
format!("{}_ptr\0", ins_value.get(0).unwrap().hdr.name().clone());
let name = format!("{}\0", ins_value.get(0).unwrap().hdr.name().clone());
let calloc_fn = LLVMGetNamedFunction(
let muentry_alloc_mmtk_fn = LLVMGetNamedFunction(
llvm_internal_context.module,
"calloc\0".as_ptr() as *const c_char
"muentry_alloc_mmtk\0".as_ptr() as *const c_char
);
if calloc_fn.is_null() {
if muentry_alloc_mmtk_fn.is_null() {
panic!("Function has not been defined.");
}
let data_layout = LLVMGetModuleDataLayout(llvm_internal_context.module);
let size = LLVMABISizeOfType(data_layout, llvm_type);
let size = LLVMConstInt(LLVMInt64Type(), size, false as i32);
let alignment = LLVMABIAlignmentOfType(data_layout, llvm_type);
let alignment =
LLVMConstInt(LLVMInt64Type(), alignment as u64, false as i32);
let mut args = vec![LLVMConstInt(LLVMInt64Type(), 1, false as i32), size];
let mut args = vec![
size,
alignment,
LLVMConstInt(LLVMInt64Type(), 0, false as i32),
// LLVMConstInt(LLVMInt32Type(), mu_type as u64, 0),
];
let ptr = LLVMBuildCall(
llvm_internal_context.builder,
calloc_fn,
muentry_alloc_mmtk_fn,
args.as_mut_ptr(),
args.len() as u32,
name_ptr.as_str().as_ptr() as *const c_char
......
......@@ -33,9 +33,7 @@ use crate::instr::*;
use llvm_sys::initialization::{
LLVMInitializeCodeGen, LLVMInitializeCore, LLVMInitializeScalarOpts
};
use llvm_sys::transforms::scalar::{
LLVMAddCFGSimplificationPass, LLVMAddGVNPass
};
use llvm_sys::transforms::scalar::{LLVMAddCFGSimplificationPass, LLVMAddGVNPass, LLVMAddReassociatePass};
use std::any::Any;
use std::borrow::Borrow;
use std::collections::HashMap;
......@@ -257,8 +255,62 @@ fn visit_inst_custom(
);
},
// visited to declare function calloc
mu::ast::inst::Instruction_::New(_)
| mu::ast::inst::Instruction_::NewHybrid(_, _) => unsafe {
mu::ast::inst::Instruction_::New(_) => unsafe {
let function_name =
MuName::new(String::from("muentry_alloc_mmtk"));
let i8_type = Arc::new(MuType::new(0, MuType_::Int(8)));
let refi8_type =
Arc::new(MuType::new(0, MuType_::Ref(i8_type)));
let llvm_refi8_type =
gen_llvm_type(llvm_internal_context, &refi8_type);
let return_type = llvm_refi8_type.clone();
let arg_type = Arc::new(MuType::new(0, MuType_::Int(64)));
let llvm_arg_type =
gen_llvm_type(llvm_internal_context, &arg_type);
let llvm_arg_types = vec![
llvm_arg_type.clone(),
llvm_arg_type.clone(),
llvm_arg_type.clone(),
];
add_function_to_module(
llvm_internal_context,
function_name,
llvm_arg_types,
return_type,
false
);
let function_name =
MuName::new(String::from("llvm.experimental.stackmap"));
let i8_type = Arc::new(MuType::new(0, MuType_::Int(8)));
let refi8_type =
Arc::new(MuType::new(0, MuType_::Ref(i8_type)));
let llvm_refi8_type =
gen_llvm_type(llvm_internal_context, &refi8_type);
let i32_type = Arc::new(MuType::new(0, MuType_::Int(32)));
let llvm_i32_type =
gen_llvm_type(llvm_internal_context, &i32_type);
let i64_type = Arc::new(MuType::new(0, MuType_::Int(64)));
let llvm_i64_type =
gen_llvm_type(llvm_internal_context, &i64_type);
let return_type = LLVMVoidType();
let llvm_arg_types =
vec![llvm_i64_type.clone(), llvm_i32_type.clone()];
add_function_to_module(
llvm_internal_context,
function_name,
llvm_arg_types,
return_type,
true
);
},
mu::ast::inst::Instruction_::NewHybrid(_, _) => unsafe {
let function_name = MuName::new(String::from("calloc"));
let i8_type = Arc::new(MuType::new(0, MuType_::Int(8)));
let refi8_type =
......@@ -707,6 +759,9 @@ unsafe fn emit_llvm_asm_code(
function_type
);
// set function as GC-able
LLVMSetGC(function, "statepoint-example\0".as_ptr() as *const c_char);
llvm_internal_context.curr_fn = Some(function);
// functions can be called by their unmangled name
......@@ -786,7 +841,7 @@ unsafe fn emit_llvm_asm_code(
}
generate_llvm_ir(&llvm_internal_context, func, vm);
generate_llvm_asm(&llvm_internal_context, func, vm);
// generate_llvm_asm(&llvm_internal_context, func, vm);
llvm_internal_context.curr_fn = None;
}
......@@ -1107,11 +1162,11 @@ pub unsafe fn gen_llvm_type(
}
MuType_::UPtr(mu_type) => {
if mu_type.is_void() {
LLVMPointerType(LLVMInt8Type(), 1)
LLVMPointerType(LLVMInt8Type(), 0)
} else {
let internal_type =
gen_llvm_type(llvm_internal_context, mu_type);
LLVMPointerType(internal_type, 1)
LLVMPointerType(internal_type, 0)
}
}
MuType_::FuncRef(sig) => LLVMPointerType(
......@@ -1383,7 +1438,7 @@ unsafe fn gen_llvm_instruction(
}
mu::ast::inst::Instruction_::New(mu_type) => {
let llvm_type = gen_llvm_type(llvm_internal_context, mu_type);
gen_instr_new(llvm_internal_context, store, ins, llvm_type);
gen_instr_new(llvm_internal_context, store, ins, llvm_type, mu_type.hdr.id());
LLVMSetGC(
llvm_internal_context.curr_fn.unwrap(),
"statepoint-example\0".as_ptr() as *const c_char
......@@ -1746,13 +1801,15 @@ unsafe fn generate_llvm_ir(
let name = CString::new(path).unwrap();
let function_pass_manager = LLVMCreateFunctionPassManagerForModule(llvm_internal_context.module);
llvm_sys::core::LLVMPrintModuleToFile(
llvm_internal_context.module,
name.as_ptr(),
ptr::null_mut()
);
optimize_ir(llvm_internal_context, func, vm);
// optimize_ir(llvm_internal_context, func, vm);
}
unsafe fn optimize_ir(
......@@ -1788,6 +1845,7 @@ unsafe fn optimize_ir(
let mut llc = Command::new(get_llvm_optimizer());
// rewrite statepoints for GC
llc.arg("-place-safepoints");
llc.arg("-rewrite-statepoints-for-gc");
// print as IR
......@@ -1818,7 +1876,7 @@ unsafe fn generate_llvm_asm(
// create 'emit' directory
mu::vm::uir_output::create_emit_directory(vm);
let input = format!("{}-opt.ll", function_name);
let input = format!("{}.ll", function_name);
let mut input_path = path::PathBuf::new();
input_path.push(&vm.vm_options.flag_aot_emit_dir);
input_path.push(&input);
......@@ -1841,6 +1899,17 @@ unsafe fn generate_llvm_asm(
// position independent code
llc.arg("-relocation-model=pic");
// disable frame pointer elimination
llc.arg("-disable-fp-elim");
// emit functions into separate sections
llc.arg("-function-sections");
// emit data into separate sections
llc.arg("-data-sections");
// llc.arg("-frame-pointer=all");
llc.arg("-filetype=asm");
llc.arg(output_command);
......
......@@ -4026,7 +4026,7 @@ macro_rules! build_and_run_llvm_test {
whitelist.push(func_id);
vm.set_primordial_thread(func_id, true, vec![]);
let image_name = format!("{}_{}", stringify!($test_name), stringify!($tester_name)) ;
let image_name = format!("{}", stringify!($tester_name)) ;
let primordial_func = vm.handle_from_func(func_id);
vm.set_primordial_thread(func_id, true, vec![]);
......
......@@ -96,6 +96,9 @@ pub mod test_exception;
#[cfg(test)]
pub mod test_floatingpoint;
#[cfg(test)]
pub mod test_gc;
#[cfg(test)]
pub mod test_global;
......
// 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.
extern crate libloading;
extern crate log;
extern crate mu;
use self::mu::ast::inst::*;
use self::mu::ast::ir::*;
use self::mu::ast::op::*;
use self::mu::ast::types::*;
use self::mu::utils::LinkedHashMap;
use self::mu::vm::*;
use crate::aot_mu;
use std::path::PathBuf;
use crate::tests::create_dyn_lib;
use std::sync::Arc;
#[test]
fn test_gc_single_call_single_obj() {
build_and_run_llvm_test!(
gc_single_call_single_obj,
gc_single_call_single_obj_test1
);
}
fn gc_single_call_single_obj() -> VM {
let opts = String::from("init_mu --generate-llvm");
let vm = VM::new_with_opts(opts.as_str());
typedef! ((vm) int1 = mu_int(1));
typedef! ((vm) int64 = mu_int(64));
typedef! ((vm) ref_int64 = mu_ref(int64));
typedef! ((vm) struct_t = mu_struct(int64, int64, ref_int64));
typedef! ((vm) ref_struct_t = mu_ref(struct_t));
funcsig! ((vm) sig = () -> ());
funcdecl! ((vm) <sig> gc_single_call_single_obj);
funcdef! ((vm) <sig> gc_single_call_single_obj VERSION gc_single_call_single_obj_v1);
block! ((vm, gc_single_call_single_obj_v1) blk_entry);
// a = NEW <struct_t>
ssa! ((vm, gc_single_call_single_obj_v1) <ref_struct_t> a);
inst! ((vm, gc_single_call_single_obj_v1) blk_entry_new1:
a = NEW <struct_t>
);
ssa! ((vm, gc_single_call_single_obj_v1) <int64> i);
inst! ((vm, gc_single_call_single_obj_v1) blk_entry_convop:
i = CONVOP (ConvOp::PTRCAST) <ref_struct_t int64> a
);
inst! ((vm, gc_single_call_single_obj_v1) blk_entry_print1:
PRINTHEX i
);
ssa! ((vm, gc_single_call_single_obj_v1) <ref_struct_t> b);
inst! ((vm, gc_single_call_single_obj_v1) blk_entry_new2:
b = NEW <struct_t>
);
ssa! ((vm, gc_single_call_single_obj_v1) <int64> i2);
inst! ((vm, gc_single_call_single_obj_v1) blk_entry_convop2:
i2 = CONVOP (ConvOp::PTRCAST) <ref_struct_t int64> b
);
inst! ((vm, gc_single_call_single_obj_v1) blk_entry_print2:
PRINTHEX i2
);
inst! ((vm, gc_single_call_single_obj_v1) blk_entry_threadexit:
THREADEXIT
);
define_block! ((vm, gc_single_call_single_obj_v1) blk_entry() {
blk_entry_new1, blk_entry_convop,
blk_entry_new2,
blk_entry_print1, blk_entry_convop2, blk_entry_print2,
blk_entry_threadexit
});
define_func_ver!((vm) gc_single_call_single_obj_v1 (entry: blk_entry) {blk_entry});
emit_test! ((vm)
gc_single_call_single_obj, gc_single_call_single_obj_test1, gc_single_call_single_obj_test1_v1,
sig,
);
vm
}
#[test]
fn test_gc_single_call_mult_obj() {
build_and_run_llvm_test!(
gc_single_call_mult_obj,
gc_single_call_mult_obj_test1
);
}
fn gc_single_call_mult_obj() -> VM {
let opts = String::from("init_mu --generate-llvm");
let vm = VM::new_with_opts(opts.as_str());
typedef! ((vm) int1 = mu_int(1));
typedef! ((vm) int64 = mu_int(64));
typedef! ((vm) ref_int64 = mu_ref(int64));
typedef! ((vm) struct_t = mu_struct(int64, int64, ref_int64));
typedef! ((vm) ref_struct_t = mu_ref(struct_t));
funcsig! ((vm) sig = () -> ());
funcdecl! ((vm) <sig> gc_single_call_mult_obj);
funcdef! ((vm) <sig> gc_single_call_mult_obj VERSION gc_single_call_mult_obj_v1);
block! ((vm, gc_single_call_mult_obj_v1) blk_entry);
// a = NEW <struct_t>
ssa! ((vm, gc_single_call_mult_obj_v1) <ref_struct_t> a);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_new1:
a = NEW <struct_t>
);
ssa! ((vm, gc_single_call_mult_obj_v1) <int64> i);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_convop:
i = CONVOP (ConvOp::PTRCAST) <ref_struct_t int64> a
);
ssa! ((vm, gc_single_call_mult_obj_v1) <ref_struct_t> b);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_new2:
b = NEW <struct_t>
);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_print1:
PRINTHEX i
);
// c = LOAD a
ssa! ((vm, gc_single_call_mult_obj_v1) <struct_t> c);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_load_a:
c = LOAD a (is_ptr: true, order: MemoryOrder::Relaxed)
);
ssa! ((vm, gc_single_call_mult_obj_v1) <int64> i2);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_convop2:
i2 = CONVOP (ConvOp::PTRCAST) <ref_struct_t int64> b
);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_print2:
PRINTHEX i2
);
inst! ((vm, gc_single_call_mult_obj_v1) blk_entry_threadexit:
THREADEXIT
);
define_block! ((vm, gc_single_call_mult_obj_v1) blk_entry() {
blk_entry_new1, blk_entry_convop,
blk_entry_new2,
blk_entry_print1, blk_entry_load_a, blk_entry_convop2, blk_entry_print2,
blk_entry_threadexit
});
define_func_ver!((vm) gc_single_call_mult_obj_v1 (entry: blk_entry) {blk_entry});
emit_test! ((vm)
gc_single_call_mult_obj, gc_single_call_mult_obj_test1, gc_single_call_mult_obj_test1_v1,
sig,
);
vm
}
#[test]
fn test_gc_mult_call_single_obj() {
build_and_run_llvm_test!(
gc_mult_call_single_obj AND foo,
gc_mult_call_single_obj_test1
);
}
fn gc_mult_call_single_obj() -> VM {
let opts = String::from("init_mu --generate-llvm");
let vm = VM::new_with_opts(opts.as_str());
typedef! ((vm) int64 = mu_int(64));
typedef! ((vm) ref_int64 = mu_ref(int64));
funcsig! ((vm) foo_sig = () -> ());
funcdecl! ((vm) <foo_sig> foo);
{
// add
funcdef! ((vm) <foo_sig> foo VERSION foo_v1);
block! ((vm, foo_v1) blk_entry);
ssa! ((vm, foo_v1) <ref_int64> x);
inst! ((vm, foo_v1) blk_entry_new:
x = NEW <int64>
);
ssa! ((vm, foo_v1) <int64> i);
inst! ((vm, foo_v1) blk_entry_convop:
i = CONVOP (ConvOp::PTRCAST) <ref_int64 int64> x
);
inst! ((vm, foo_v1) blk_entry_print1:
PRINTHEX i
);
ssa! ((vm, foo_v1) <int64> y);
inst! ((vm, foo_v1) blk_entry_load_x:
y = LOAD x (is_ptr: true, order: MemoryOrder::Relaxed)
);
inst! ((vm, foo_v1) blk_entry_ret:
RET
);
define_block! ((vm, foo_v1) blk_entry() {
blk_entry_new, blk_entry_convop, blk_entry_print1,
blk_entry_load_x, blk_entry_ret
});
define_func_ver!((vm) foo_v1 (entry: blk_entry) {blk_entry});
}
typedef! ((vm) int1 = mu_int(1));
typedef! ((vm) struct_t = mu_struct(int64, int64, ref_int64));
typedef! ((vm) ref_struct_t = mu_ref(struct_t));
funcsig! ((vm) sig = () -> ());
funcdecl! ((vm) <sig> gc_mult_call_single_obj);
funcdef! ((vm) <sig> gc_mult_call_single_obj VERSION gc_mult_call_single_obj_v1);
block! ((vm, gc_mult_call_single_obj_v1) blk_entry);
// a = NEW <struct_t>
ssa! ((vm, gc_mult_call_single_obj_v1) <ref_struct_t> a);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_new1:
a = NEW <struct_t>
);
ssa! ((vm, gc_mult_call_single_obj_v1) <int64> i);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_convop:
i = CONVOP (ConvOp::PTRCAST) <ref_struct_t int64> a
);
ssa! ((vm, gc_mult_call_single_obj_v1) <ref_struct_t> b);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_new2:
b = NEW <struct_t>
);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_print1:
PRINTHEX i
);
typedef! ((vm) funcref_to_sig = mu_funcref(foo_sig));
constdef! ((vm) <funcref_to_sig> funcref_foo = Constant::FuncRef(foo));
consta! ((vm, gc_mult_call_single_obj_v1) funcref_foo_local = funcref_foo);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_call:
EXPRCALL (CallConvention::Mu, is_abort: false) funcref_foo_local ()
);
ssa! ((vm, gc_mult_call_single_obj_v1) <int64> i2);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_convop2:
i2 = CONVOP (ConvOp::PTRCAST) <ref_struct_t int64> b
);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_print2:
PRINTHEX i2
);
inst! ((vm, gc_mult_call_single_obj_v1) blk_entry_threadexit:
THREADEXIT
);
define_block! ((vm, gc_mult_call_single_obj_v1) blk_entry() {
blk_entry_new1, blk_entry_convop,
blk_entry_new2,
blk_entry_print1, blk_entry_call, blk_entry_convop2, blk_entry_print2,
blk_entry_threadexit
});
define_func_ver!((vm) gc_mult_call_single_obj_v1 (entry: blk_entry) {blk_entry});
emit_test! ((vm)
gc_mult_call_single_obj, gc_mult_call_single_obj_test1, gc_mult_call_single_obj_test1_v1,
sig,
);
vm
}
#[test]
fn test_gc_mult_call_single_obj_with_gc() {
build_and_run_llvm_test!(
gc_mult_call_single_obj_with_gc AND foo,
gc_mult_call_single_obj_with_gc_test1
);
}
fn gc_mult_call_single_obj_with_gc() -> VM {
let opts = String::from("init_mu --generate-llvm");
let vm = VM::new_with_opts(opts.as_str());
typedef! ((vm) int64 = mu_int(64));
typedef! ((vm) ref_int64 = mu_ref(int64));
typedef! ((vm) iref_int64 = mu_iref(int64));
typedef! ((vm) iref_ref_int64 = mu_iref(ref_int64));
funcsig! ((vm) foo_sig = () -> ());
funcdecl! ((vm) <foo_sig> foo);
{
// add
funcdef! ((vm) <foo_sig> foo VERSION foo_v1);
block! ((vm, foo_v1) blk_entry);
ssa! ((vm, foo_v1) <ref_int64> x);
inst! ((vm, foo_v1) blk_entry_new:
x = NEW <int64>
);
ssa! ((vm, foo_v1) <int64> i);
inst! ((vm, foo_v1) blk_entry_convop:
i = CONVOP (ConvOp::PTRCAST) <ref_int64 int64> x
);