use crate::{
adapters::FunctionMapper,
backends::{Backend, GeneratedFiles},
config::GaiaConfig,
instruction::{CoreInstruction, GaiaInstruction},
program::{GaiaConstant, GaiaFunction, GaiaGlobal, GaiaModule},
types::{GaiaType, mapping},
};
use gaia_types::{
helpers::{AbiCompatible, ApiCompatible, Architecture, ArtifactType, CompilationTarget},
GaiaError, Result,
};
use std::collections::HashMap;
#[cfg(feature = "jvm-assembler")]
use jvm_assembler::{
formats::class::writer::ClassWriter,
program::{JvmAccessFlags, JvmField, JvmInstruction, JvmMethod, JvmProgram, JvmVersion, JvmExceptionHandler},
};
#[cfg(not(feature = "jvm-assembler"))]
mod jvm_stub {
pub struct JvmProgram;
}
#[cfg(not(feature = "jvm-assembler"))]
use jvm_stub::*;
#[derive(Default)]
pub struct JvmBackend {}
impl Backend for JvmBackend {
fn name(&self) -> &'static str {
"JVM"
}
fn primary_target(&self) -> CompilationTarget {
CompilationTarget { build: Architecture::JVM, host: AbiCompatible::Unknown, target: ApiCompatible::JvmRuntime(8) }
}
fn artifact_type(&self) -> ArtifactType {
ArtifactType::Bytecode
}
fn match_score(&self, target: &CompilationTarget) -> f32 {
match target.build {
Architecture::JVM => match target.host {
AbiCompatible::Unknown => 80.0,
AbiCompatible::JavaAssembly => 5.0,
_ => -100.0,
},
_ => -100.0,
}
}
fn generate(&self, program: &GaiaModule, config: &GaiaConfig) -> Result<GeneratedFiles> {
#[cfg(feature = "jvm-assembler")]
{
let mut files = HashMap::new();
let jvm_program = convert_gaia_to_jvm(program, config)?;
match config.target.host {
AbiCompatible::Unknown => {
let buffer = Vec::new();
let class_writer = ClassWriter::new(buffer);
let class_bytes = class_writer.write(&jvm_program).result?;
files.insert("main.class".to_string(), class_bytes);
}
AbiCompatible::JavaAssembly => {
return Err(GaiaError::custom_error("JASM output is currently disabled"));
}
_ => return Err(GaiaError::custom_error(&format!("Unsupported host ABI: {:?}", config.target.host))),
}
Ok(GeneratedFiles { artifact_type: self.artifact_type(), files, custom: None, diagnostics: vec![] })
}
#[cfg(not(feature = "jvm-assembler"))]
{
let _ = program;
let _ = config;
Err(gaia_types::errors::GaiaError::custom_error("JVM backend not enabled"))
}
}
}
impl JvmBackend {
pub fn generate_program(program: &GaiaModule) -> Result<JvmProgram> {
#[cfg(feature = "jvm-assembler")]
{
let default_config = GaiaConfig::default();
convert_gaia_to_jvm(program, &default_config)
}
#[cfg(not(feature = "jvm-assembler"))]
{
let _ = program;
Err(gaia_types::errors::GaiaError::custom_error("JVM backend not enabled"))
}
}
}
#[cfg(feature = "jvm-assembler")]
struct JvmContext {
function_mapper: FunctionMapper,
field_types: HashMap<(String, String), String>,
}
#[cfg(feature = "jvm-assembler")]
fn convert_gaia_to_jvm(program: &GaiaModule, config: &GaiaConfig) -> Result<JvmProgram> {
let mut jvm_program = JvmProgram::new(program.name.clone());
jvm_program.version = JvmVersion { major: 52, minor: 0 };
jvm_program.access_flags = JvmAccessFlags::public();
let mut field_types = HashMap::new();
for class in &program.classes {
for field in &class.fields {
field_types.insert((class.name.clone(), field.name.clone()), convert_gaia_type_to_jvm_descriptor(&field.ty));
}
}
for global in &program.globals {
field_types.insert(("Main".to_string(), global.name.clone()), convert_gaia_type_to_jvm_descriptor(&global.ty));
}
let ctx = JvmContext { function_mapper: FunctionMapper::from_config(&config.setting)?, field_types };
for function in &program.functions {
let jvm_method = convert_gaia_function_to_jvm(function, &ctx)?;
jvm_program.add_method(jvm_method);
}
for class in &program.classes {
for field in &class.fields {
let jvm_field = convert_gaia_field_to_jvm_field(field)?;
jvm_program.add_field(jvm_field);
}
for method in &class.methods {
let jvm_method = convert_gaia_function_to_jvm(method, &ctx)?;
jvm_program.add_method(jvm_method);
}
}
for global in &program.globals {
let jvm_field = convert_gaia_global_to_jvm_field(global)?;
jvm_program.add_field(jvm_field);
}
Ok(jvm_program)
}
#[cfg(feature = "jvm-assembler")]
fn convert_gaia_function_to_jvm(function: &GaiaFunction, ctx: &JvmContext) -> Result<JvmMethod> {
let descriptor = build_method_descriptor(&function.signature.params, &Some(function.signature.return_type.clone()));
let mut method = JvmMethod::new(function.name.clone(), descriptor);
method.access_flags.is_public = true;
method.access_flags.is_static = true;
let mut try_blocks: Vec<()> = vec![];
for block in &function.blocks {
if !block.label.is_empty() {
method.add_instruction(JvmInstruction::Label { name: block.label.clone() });
}
let mut block_instructions = vec![];
for instruction in &block.instructions {
block_instructions.push(instruction.clone());
}
let optimized_block_instructions = constant_fold(&block_instructions);
let optimized_block_instructions = eliminate_dead_code(&optimized_block_instructions);
for instruction in &optimized_block_instructions {
match instruction {
_ => {
let converted = convert_gaia_instruction_to_jvm(instruction, ctx)?;
for instr in converted {
method.add_instruction(instr);
}
}
}
}
let mut terminator_instructions = vec![];
match &block.terminator {
crate::program::GaiaTerminator::Jump(label) => {
terminator_instructions.push(JvmInstruction::Goto { target: label.clone() });
}
crate::program::GaiaTerminator::Branch { true_label, false_label } => {
terminator_instructions.push(JvmInstruction::Ifne { target: true_label.clone() });
terminator_instructions.push(JvmInstruction::Goto { target: false_label.clone() });
}
crate::program::GaiaTerminator::Return => {
match function.signature.return_type {
GaiaType::I32 | GaiaType::U32 | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 | GaiaType::Bool => {
terminator_instructions.push(JvmInstruction::Ireturn);
}
GaiaType::I64 | GaiaType::U64 => {
terminator_instructions.push(JvmInstruction::Lreturn);
}
GaiaType::F32 => {
terminator_instructions.push(JvmInstruction::Freturn);
}
GaiaType::F64 => {
terminator_instructions.push(JvmInstruction::Dreturn);
}
_ => {
terminator_instructions.push(JvmInstruction::Areturn);
}
}
}
crate::program::GaiaTerminator::Call { callee, args_count: _, next_block } => {
let jvm_target = CompilationTarget {
build: Architecture::JVM,
host: AbiCompatible::JavaAssembly,
target: ApiCompatible::JvmRuntime(8),
};
let mapped = ctx.function_mapper.map_function(&jvm_target, callee).unwrap_or(callee.as_str()).to_string();
terminator_instructions.push(JvmInstruction::Invokestatic {
class_name: "Main".to_string(),
method_name: mapped,
descriptor: "()V".to_string(), });
terminator_instructions.push(JvmInstruction::Goto { target: next_block.clone() });
}
crate::program::GaiaTerminator::Halt => {
terminator_instructions.push(JvmInstruction::Iconst0);
terminator_instructions.push(JvmInstruction::Invokestatic {
class_name: "java/lang/System".to_string(),
method_name: "exit".to_string(),
descriptor: "(I)V".to_string(),
});
}
}
let optimized_terminator_instructions = optimize_jvm_instructions(terminator_instructions);
for instr in optimized_terminator_instructions {
method.add_instruction(instr);
}
}
let (max_stack, max_locals) = calculate_stack_and_locals(function, ctx)?;
method.max_stack = max_stack;
method.max_locals = max_locals;
Ok(method)
}
#[cfg(feature = "jvm-assembler")]
fn convert_gaia_field_to_jvm_field(field: &crate::program::GaiaField) -> Result<JvmField> {
let descriptor = convert_gaia_type_to_jvm_descriptor(&field.ty);
let mut jvm_field = JvmField::new(field.name.clone(), descriptor);
if field.is_static {
jvm_field.access_flags.is_static = true;
}
match field.visibility {
crate::program::Visibility::Public => jvm_field.access_flags.is_public = true,
crate::program::Visibility::Private => jvm_field.access_flags.is_private = true,
crate::program::Visibility::Protected => jvm_field.access_flags.is_protected = true,
_ => {}
}
Ok(jvm_field)
}
#[cfg(feature = "jvm-assembler")]
fn convert_gaia_global_to_jvm_field(global: &GaiaGlobal) -> Result<JvmField> {
let descriptor = convert_gaia_type_to_jvm_descriptor(&global.ty);
let mut field = JvmField::new(global.name.clone(), descriptor);
field.access_flags.is_public = true;
field.access_flags.is_static = true;
Ok(field)
}
#[cfg(feature = "jvm-assembler")]
fn constant_fold(instructions: &[GaiaInstruction]) -> Vec<GaiaInstruction> {
let mut optimized = vec![];
let mut i = 0;
while i < instructions.len() {
if i + 2 < instructions.len() {
if let (GaiaInstruction::Core(CoreInstruction::PushConstant(c1)),
GaiaInstruction::Core(CoreInstruction::PushConstant(c2)),
GaiaInstruction::Core(op)) = (&instructions[i], &instructions[i+1], &instructions[i+2]) {
match op {
CoreInstruction::Add(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a + b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
CoreInstruction::Sub(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a - b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
CoreInstruction::Mul(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a * b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
CoreInstruction::Div(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a / b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
CoreInstruction::Rem(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a % b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
CoreInstruction::And(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a & b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
CoreInstruction::Or(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a | b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
CoreInstruction::Xor(ty) => {
if let (Some(result), _) = evaluate_binary_op(c1, c2, |a, b| a ^ b) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 3;
continue;
}
}
_ => {}
}
}
}
if i + 1 < instructions.len() {
if let (GaiaInstruction::Core(CoreInstruction::PushConstant(c)),
GaiaInstruction::Core(op)) = (&instructions[i], &instructions[i+1]) {
match op {
CoreInstruction::Neg(ty) => {
if let Some(result) = evaluate_unary_op(c, |a| -a) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 2;
continue;
}
}
CoreInstruction::Not(ty) => {
if let Some(result) = evaluate_unary_op(c, |a| !a) {
optimized.push(GaiaInstruction::Core(CoreInstruction::PushConstant(result)));
i += 2;
continue;
}
}
_ => {}
}
}
}
optimized.push(instructions[i].clone());
i += 1;
}
optimized
}
#[cfg(feature = "jvm-assembler")]
fn evaluate_binary_op<F>(c1: &GaiaConstant, c2: &GaiaConstant, op: F) -> (Option<GaiaConstant>, GaiaType)
where F: Fn(i64, i64) -> i64 {
match (c1, c2) {
(GaiaConstant::I32(a), GaiaConstant::I32(b)) => {
let result = op(*a as i64, *b as i64);
(Some(GaiaConstant::I32(result as i32)), GaiaType::I32)
}
(GaiaConstant::I64(a), GaiaConstant::I64(b)) => {
let result = op(*a, *b);
(Some(GaiaConstant::I64(result)), GaiaType::I64)
}
(GaiaConstant::U32(a), GaiaConstant::U32(b)) => {
let result = op(*a as i64, *b as i64);
(Some(GaiaConstant::U32(result as u32)), GaiaType::U32)
}
(GaiaConstant::U64(a), GaiaConstant::U64(b)) => {
let result = op(*a as i64, *b as i64);
(Some(GaiaConstant::U64(result as u64)), GaiaType::U64)
}
_ => (None, GaiaType::I32)
}
}
#[cfg(feature = "jvm-assembler")]
fn evaluate_unary_op<F>(c: &GaiaConstant, op: F) -> Option<GaiaConstant>
where F: Fn(i64) -> i64 {
match c {
GaiaConstant::I32(a) => {
let result = op(*a as i64);
Some(GaiaConstant::I32(result as i32))
}
GaiaConstant::I64(a) => {
let result = op(*a);
Some(GaiaConstant::I64(result))
}
GaiaConstant::U32(a) => {
let result = op(*a as i64);
Some(GaiaConstant::U32(result as u32))
}
GaiaConstant::U64(a) => {
let result = op(*a as i64);
Some(GaiaConstant::U64(result as u64))
}
GaiaConstant::Bool(a) => {
Some(GaiaConstant::Bool(!*a))
}
_ => None
}
}
#[cfg(feature = "jvm-assembler")]
fn eliminate_dead_code(instructions: &[GaiaInstruction]) -> Vec<GaiaInstruction> {
let mut optimized = vec![];
let mut reachable = true;
let mut i = 0;
while i < instructions.len() {
match &instructions[i] {
GaiaInstruction::Core(CoreInstruction::Br(label)) => {
optimized.push(instructions[i].clone());
reachable = false;
i += 1;
}
GaiaInstruction::Core(CoreInstruction::BrTrue(label)) |
GaiaInstruction::Core(CoreInstruction::BrFalse(label)) => {
optimized.push(instructions[i].clone());
i += 1;
}
GaiaInstruction::Core(CoreInstruction::Ret) => {
optimized.push(instructions[i].clone());
reachable = false;
i += 1;
}
GaiaInstruction::Core(CoreInstruction::Label(name)) => {
optimized.push(instructions[i].clone());
reachable = true;
i += 1;
}
_ => {
if reachable {
optimized.push(instructions[i].clone());
}
i += 1;
}
}
}
optimized
}
#[cfg(feature = "jvm-assembler")]
fn optimize_jvm_instructions(instructions: Vec<JvmInstruction>) -> Vec<JvmInstruction> {
let mut optimized = vec![];
let mut i = 0;
while i < instructions.len() {
match &instructions[i] {
JvmInstruction::Bipush { value } if *value >= -1 && *value <= 5 => {
match *value {
-1 => optimized.push(JvmInstruction::IconstM1),
0 => optimized.push(JvmInstruction::Iconst0),
1 => optimized.push(JvmInstruction::Iconst1),
2 => optimized.push(JvmInstruction::Iconst2),
3 => optimized.push(JvmInstruction::Iconst3),
4 => optimized.push(JvmInstruction::Iconst4),
5 => optimized.push(JvmInstruction::Iconst5),
_ => optimized.push(instructions[i].clone()),
}
i += 1;
continue;
}
JvmInstruction::Iload { index } if *index <= 3 => {
match *index {
0 => optimized.push(JvmInstruction::Iload0),
1 => optimized.push(JvmInstruction::Iload1),
2 => optimized.push(JvmInstruction::Iload2),
3 => optimized.push(JvmInstruction::Iload3),
_ => optimized.push(instructions[i].clone()),
}
i += 1;
continue;
}
JvmInstruction::Istore { index } if *index <= 3 => {
match *index {
0 => optimized.push(JvmInstruction::Istore0),
1 => optimized.push(JvmInstruction::Istore1),
2 => optimized.push(JvmInstruction::Istore2),
3 => optimized.push(JvmInstruction::Istore3),
_ => optimized.push(instructions[i].clone()),
}
i += 1;
continue;
}
JvmInstruction::Pop => {
if !optimized.is_empty() {
match optimized.last().unwrap() {
JvmInstruction::Iconst0 | JvmInstruction::Iconst1 | JvmInstruction::Iconst2 |
JvmInstruction::Iconst3 | JvmInstruction::Iconst4 | JvmInstruction::Iconst5 |
JvmInstruction::IconstM1 | JvmInstruction::AconstNull |
JvmInstruction::Fconst0 | JvmInstruction::Fconst1 | JvmInstruction::Fconst2 |
JvmInstruction::Dconst0 | JvmInstruction::Dconst1 | JvmInstruction::Lconst0 |
JvmInstruction::Lconst1 => {
optimized.pop();
i += 1;
continue;
}
_ => {}
}
}
optimized.push(instructions[i].clone());
i += 1;
}
_ => {
if !optimized.is_empty() && optimized.last().unwrap() == &instructions[i] {
i += 1;
continue;
}
optimized.push(instructions[i].clone());
i += 1;
}
}
}
optimized
}
#[cfg(feature = "jvm-assembler")]
fn convert_gaia_instruction_to_jvm(instruction: &GaiaInstruction, ctx: &JvmContext) -> Result<Vec<JvmInstruction>> {
match instruction {
GaiaInstruction::Core(core) => {
match core {
CoreInstruction::PushConstant(constant) => match constant {
GaiaConstant::I8(value) => Ok(vec![JvmInstruction::Bipush { value: *value }]),
GaiaConstant::U8(value) => Ok(vec![JvmInstruction::Bipush { value: *value as i8 }]),
GaiaConstant::I16(value) => Ok(vec![JvmInstruction::Sipush { value: *value }]),
GaiaConstant::U16(value) => Ok(vec![JvmInstruction::Sipush { value: *value as i16 }]),
GaiaConstant::I32(value) => match *value {
0 => Ok(vec![JvmInstruction::Iconst0]),
1 => Ok(vec![JvmInstruction::Iconst1]),
2 => Ok(vec![JvmInstruction::Iconst2]),
3 => Ok(vec![JvmInstruction::Iconst3]),
4 => Ok(vec![JvmInstruction::Iconst4]),
5 => Ok(vec![JvmInstruction::Iconst5]),
-1 => Ok(vec![JvmInstruction::IconstM1]),
_ if *value >= -128 && *value <= 127 => Ok(vec![JvmInstruction::Bipush { value: *value as i8 }]),
_ if *value >= -32768 && *value <= 32767 => Ok(vec![JvmInstruction::Sipush { value: *value as i16 }]),
_ => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
},
GaiaConstant::U32(value) => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
GaiaConstant::I64(value) => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
GaiaConstant::U64(value) => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
GaiaConstant::F32(value) => match *value {
0.0 => Ok(vec![JvmInstruction::Fconst0]),
1.0 => Ok(vec![JvmInstruction::Fconst1]),
2.0 => Ok(vec![JvmInstruction::Fconst2]),
_ => Ok(vec![JvmInstruction::Ldc { symbol: value.to_string() }]),
},
GaiaConstant::F64(value) => match *value {
0.0 => Ok(vec![JvmInstruction::Dconst0]),
1.0 => Ok(vec![JvmInstruction::Dconst1]),
_ => Ok(vec![JvmInstruction::Ldc2W { symbol: value.to_string() }]),
},
GaiaConstant::String(value) => Ok(vec![JvmInstruction::Ldc { symbol: value.clone() }]),
GaiaConstant::Bool(value) => Ok(vec![if *value { JvmInstruction::Iconst1 } else { JvmInstruction::Iconst0 }]),
GaiaConstant::Null => Ok(vec![JvmInstruction::AconstNull]),
_ => Err(GaiaError::custom_error("Unsupported constant type for JVM")),
},
CoreInstruction::Load(gaia_type) => {
Err(GaiaError::custom_error(&format!("JVM indirect load not supported for type: {:?}", gaia_type)))
},
CoreInstruction::Store(gaia_type) => {
Err(GaiaError::custom_error(&format!("JVM indirect store not supported for type: {:?}", gaia_type)))
},
CoreInstruction::Add(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Iadd,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Ladd,
GaiaType::F32 => JvmInstruction::Fadd,
GaiaType::F64 => JvmInstruction::Dadd,
_ => JvmInstruction::Iadd,
}]),
CoreInstruction::Sub(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Isub,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lsub,
GaiaType::F32 => JvmInstruction::Fsub,
GaiaType::F64 => JvmInstruction::Dsub,
_ => JvmInstruction::Isub,
}]),
CoreInstruction::Mul(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Imul,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lmul,
GaiaType::F32 => JvmInstruction::Fmul,
GaiaType::F64 => JvmInstruction::Dmul,
_ => JvmInstruction::Imul,
}]),
CoreInstruction::Div(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Idiv,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Ldiv,
GaiaType::F32 => JvmInstruction::Fdiv,
GaiaType::F64 => JvmInstruction::Ddiv,
_ => JvmInstruction::Idiv,
}]),
CoreInstruction::Rem(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Irem,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lrem,
GaiaType::F32 => JvmInstruction::Frem,
GaiaType::F64 => JvmInstruction::Drem,
_ => JvmInstruction::Irem,
}]),
CoreInstruction::Pop => Ok(vec![JvmInstruction::Pop]),
CoreInstruction::Ret => Ok(vec![JvmInstruction::Return]),
CoreInstruction::Br(label) => Ok(vec![JvmInstruction::Goto { target: label.clone() }]),
CoreInstruction::BrTrue(label) => Ok(vec![JvmInstruction::Ifne { target: label.clone() }]),
CoreInstruction::BrFalse(label) => Ok(vec![JvmInstruction::Ifeq { target: label.clone() }]),
CoreInstruction::Label(name) => Ok(vec![JvmInstruction::Label { name: name.clone() }]),
CoreInstruction::Call(name, arg_count) => {
let jvm_target = gaia_types::helpers::CompilationTarget {
build: gaia_types::helpers::Architecture::JVM,
host: gaia_types::helpers::AbiCompatible::JavaAssembly,
target: gaia_types::helpers::ApiCompatible::JvmRuntime(8),
};
let mapped = ctx.function_mapper.map_function(&jvm_target, name).unwrap_or(name);
let mut descriptor = "()V".to_string();
if name.starts_with("java/") || name.starts_with("java.lang/") {
let parts: Vec<&str> = name.split('.').collect();
if parts.len() >= 2 {
let class_name = parts[0..parts.len()-1].join("/");
let method_name = parts[parts.len()-1];
descriptor = match (class_name.as_str(), method_name, *arg_count) {
("java/lang/System", "exit", 1) => "(I)V".to_string(),
("java/lang/System", "currentTimeMillis", 0) => "()J".to_string(),
("java/lang/System", "arraycopy", 5) => "(Ljava/lang/Object;ILjava/lang/Object;II)V".to_string(),
("java/lang/String", "valueOf", 1) => "(I)Ljava/lang/String;".to_string(),
("java/lang/Integer", "parseInt", 1) => "(Ljava/lang/String;)I".to_string(),
_ => format!("({})V", "I".repeat(*arg_count)),
};
Ok(vec![JvmInstruction::Invokestatic {
class_name,
method_name: method_name.to_string(),
descriptor,
}])
} else {
Ok(vec![JvmInstruction::Invokestatic {
class_name: "Main".to_string(),
method_name: mapped.to_string(),
descriptor,
}])
}
} else {
descriptor = format!("({})V", "I".repeat(*arg_count));
if *arg_count > 255 {
return Err(GaiaError::custom_error("Too many arguments for JVM method call"));
}
Ok(vec![JvmInstruction::Invokestatic {
class_name: "Main".to_string(),
method_name: mapped.to_string(),
descriptor,
}])
}
},
CoreInstruction::LoadLocal(index, ty) => {
if *index > 65535 {
return Err(GaiaError::custom_error("Local variable index out of range for JVM"));
}
Ok(vec![match ty {
GaiaType::I32
| GaiaType::U32
| GaiaType::Bool
| GaiaType::I8
| GaiaType::U8
| GaiaType::I16
| GaiaType::U16 => JvmInstruction::Iload { index: *index as u16 },
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lload { index: *index as u16 },
GaiaType::F32 => JvmInstruction::Fload { index: *index as u16 },
GaiaType::F64 => JvmInstruction::Dload { index: *index as u16 },
_ => JvmInstruction::Aload { index: *index as u16 },
}])
},
CoreInstruction::StoreLocal(index, ty) => {
if *index > 65535 {
return Err(GaiaError::custom_error("Local variable index out of range for JVM"));
}
Ok(vec![match ty {
GaiaType::I32
| GaiaType::U32
| GaiaType::Bool
| GaiaType::I8
| GaiaType::U8
| GaiaType::I16
| GaiaType::U16 => JvmInstruction::Istore { index: *index as u16 },
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lstore { index: *index as u16 },
GaiaType::F32 => JvmInstruction::Fstore { index: *index as u16 },
GaiaType::F64 => JvmInstruction::Dstore { index: *index as u16 },
_ => JvmInstruction::Astore { index: *index as u16 },
}])
},
CoreInstruction::LoadArg(index, ty) => {
if *index > 65535 {
return Err(GaiaError::custom_error("Argument index out of range for JVM"));
}
Ok(vec![match ty {
GaiaType::I32
| GaiaType::U32
| GaiaType::Bool
| GaiaType::I8
| GaiaType::U8
| GaiaType::I16
| GaiaType::U16 => JvmInstruction::Iload { index: *index as u16 },
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lload { index: *index as u16 },
GaiaType::F32 => JvmInstruction::Fload { index: *index as u16 },
GaiaType::F64 => JvmInstruction::Dload { index: *index as u16 },
_ => JvmInstruction::Aload { index: *index as u16 },
}])
},
CoreInstruction::StoreArg(index, ty) => {
if *index > 65535 {
return Err(GaiaError::custom_error("Argument index out of range for JVM"));
}
Ok(vec![match ty {
GaiaType::I32
| GaiaType::U32
| GaiaType::Bool
| GaiaType::I8
| GaiaType::U8
| GaiaType::I16
| GaiaType::U16 => JvmInstruction::Istore { index: *index as u16 },
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lstore { index: *index as u16 },
GaiaType::F32 => JvmInstruction::Fstore { index: *index as u16 },
GaiaType::F64 => JvmInstruction::Dstore { index: *index as u16 },
_ => JvmInstruction::Astore { index: *index as u16 },
}])
},
CoreInstruction::New(type_name) => Ok(vec![
JvmInstruction::New { class_name: type_name.replace('.', "/") },
JvmInstruction::Dup,
JvmInstruction::Invokespecial {
class_name: type_name.replace('.', "/"),
method_name: "<init>".to_string(),
descriptor: "()V".to_string(),
},
]),
CoreInstruction::LoadField(type_name, field_name) => {
let descriptor = ctx
.field_types
.get(&(type_name.clone(), field_name.clone()))
.cloned()
.unwrap_or_else(|| "Ljava/lang/Object;".to_string());
Ok(vec![JvmInstruction::Getfield {
class_name: type_name.replace('.', "/"),
field_name: field_name.to_string(),
descriptor,
}])
}
CoreInstruction::StoreField(type_name, field_name) => {
let descriptor = ctx
.field_types
.get(&(type_name.clone(), field_name.clone()))
.cloned()
.unwrap_or_else(|| "Ljava/lang/Object;".to_string());
Ok(vec![JvmInstruction::Putfield {
class_name: type_name.replace('.', "/"),
field_name: field_name.to_string(),
descriptor,
}])
}
CoreInstruction::LoadElement(ty) => {
let mut instructions = vec![];
instructions.push(JvmInstruction::Dup);
instructions.push(JvmInstruction::Arraylength);
instructions.push(JvmInstruction::Swap);
instructions.push(JvmInstruction::Swap);
instructions.push(JvmInstruction::Lcmp);
instructions.push(JvmInstruction::Ifge { target: "array_index_out_of_bounds".to_string() });
instructions.push(match ty {
GaiaType::I32 | GaiaType::U32 => JvmInstruction::Iaload,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Laload,
GaiaType::F32 => JvmInstruction::Faload,
GaiaType::F64 => JvmInstruction::Daload,
GaiaType::I8 | GaiaType::U8 | GaiaType::Bool => JvmInstruction::Baload,
GaiaType::I16 | GaiaType::U16 => JvmInstruction::Saload,
_ => JvmInstruction::Aaload,
});
instructions.push(JvmInstruction::Label { name: "array_index_out_of_bounds".to_string() });
instructions.push(JvmInstruction::New { class_name: "java/lang/ArrayIndexOutOfBoundsException".to_string() });
instructions.push(JvmInstruction::Dup);
instructions.push(JvmInstruction::Invokespecial { class_name: "java/lang/ArrayIndexOutOfBoundsException".to_string(), method_name: "<init>".to_string(), descriptor: "()V".to_string() });
instructions.push(JvmInstruction::Athrow);
Ok(instructions)
},
CoreInstruction::StoreElement(ty) => {
let mut instructions = vec![];
instructions.push(JvmInstruction::Dup2);
instructions.push(JvmInstruction::Arraylength);
instructions.push(JvmInstruction::Swap);
instructions.push(JvmInstruction::Swap);
instructions.push(JvmInstruction::Swap);
instructions.push(JvmInstruction::Lcmp);
instructions.push(JvmInstruction::Ifge { target: "array_index_out_of_bounds_store".to_string() });
instructions.push(match ty {
GaiaType::I32 | GaiaType::U32 => JvmInstruction::Iastore,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lastore,
GaiaType::F32 => JvmInstruction::Fastore,
GaiaType::F64 => JvmInstruction::Dastore,
GaiaType::I8 | GaiaType::U8 | GaiaType::Bool => JvmInstruction::Bastore,
GaiaType::I16 | GaiaType::U16 => JvmInstruction::Sastore,
_ => JvmInstruction::Aastore,
});
instructions.push(JvmInstruction::Label { name: "array_index_out_of_bounds_store".to_string() });
instructions.push(JvmInstruction::New { class_name: "java/lang/ArrayIndexOutOfBoundsException".to_string() });
instructions.push(JvmInstruction::Dup);
instructions.push(JvmInstruction::Invokespecial { class_name: "java/lang/ArrayIndexOutOfBoundsException".to_string(), method_name: "<init>".to_string(), descriptor: "()V".to_string() });
instructions.push(JvmInstruction::Athrow);
Ok(instructions)
},
CoreInstruction::Cmp(condition, ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Lcmp,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lcmp,
GaiaType::F32 => JvmInstruction::Fcmpg,
GaiaType::F64 => JvmInstruction::Dcmpg,
_ => JvmInstruction::Lcmp,
}]),
CoreInstruction::Gep { base_type, indices } => Err(GaiaError::not_implemented("JVM GEP")),
CoreInstruction::NewArray(ty, is_length_on_stack) => {
let instructions = match ty {
GaiaType::I8 | GaiaType::U8 | GaiaType::Bool => vec![JvmInstruction::Newarray { type_code: 4 }], GaiaType::I16 | GaiaType::U16 => vec![JvmInstruction::Newarray { type_code: 5 }], GaiaType::I32 | GaiaType::U32 => vec![JvmInstruction::Newarray { type_code: 6 }], GaiaType::I64 | GaiaType::U64 => vec![JvmInstruction::Newarray { type_code: 7 }], GaiaType::F32 => vec![JvmInstruction::Newarray { type_code: 8 }], GaiaType::F64 => vec![JvmInstruction::Newarray { type_code: 9 }], GaiaType::Object | GaiaType::String => {
vec![JvmInstruction::Anewarray { class_name: ty.to_string().replace('.', "/") }]
},
_ => vec![JvmInstruction::Newarray { type_code: 6 }], };
Ok(instructions)
},
CoreInstruction::ArrayLength => Ok(vec![JvmInstruction::Arraylength]),
CoreInstruction::ArrayPush => {
Ok(vec![
JvmInstruction::Dup, JvmInstruction::Arraylength, JvmInstruction::Swap, JvmInstruction::Swap, JvmInstruction::Iastore, ])
},
CoreInstruction::Cast { from, to, kind } => {
let mut instructions = vec![];
match (from, to) {
(GaiaType::I8, GaiaType::I16) | (GaiaType::U8, GaiaType::I16) | (GaiaType::I8, GaiaType::I32) | (GaiaType::U8, GaiaType::I32) | (GaiaType::I16, GaiaType::I32) | (GaiaType::U16, GaiaType::I32) => {
}
(GaiaType::I8, GaiaType::I64) | (GaiaType::U8, GaiaType::I64) | (GaiaType::I16, GaiaType::I64) | (GaiaType::U16, GaiaType::I64) | (GaiaType::I32, GaiaType::I64) | (GaiaType::U32, GaiaType::I64) => {
instructions.push(JvmInstruction::I2l);
}
(GaiaType::I8, GaiaType::F32) | (GaiaType::U8, GaiaType::F32) | (GaiaType::I16, GaiaType::F32) | (GaiaType::U16, GaiaType::F32) | (GaiaType::I32, GaiaType::F32) | (GaiaType::U32, GaiaType::F32) => {
instructions.push(JvmInstruction::I2f);
}
(GaiaType::I8, GaiaType::F64) | (GaiaType::U8, GaiaType::F64) | (GaiaType::I16, GaiaType::F64) | (GaiaType::U16, GaiaType::F64) | (GaiaType::I32, GaiaType::F64) | (GaiaType::U32, GaiaType::F64) => {
instructions.push(JvmInstruction::I2d);
}
(GaiaType::I64, GaiaType::I32) => {
instructions.push(JvmInstruction::L2i);
}
(GaiaType::I64, GaiaType::F32) => {
instructions.push(JvmInstruction::L2f);
}
(GaiaType::I64, GaiaType::F64) => {
instructions.push(JvmInstruction::L2d);
}
(GaiaType::F32, GaiaType::I32) => {
instructions.push(JvmInstruction::F2i);
}
(GaiaType::F32, GaiaType::I64) => {
instructions.push(JvmInstruction::F2l);
}
(GaiaType::F32, GaiaType::F64) => {
instructions.push(JvmInstruction::F2d);
}
(GaiaType::F64, GaiaType::I32) => {
instructions.push(JvmInstruction::D2i);
}
(GaiaType::F64, GaiaType::I64) => {
instructions.push(JvmInstruction::D2l);
}
(GaiaType::F64, GaiaType::F32) => {
instructions.push(JvmInstruction::D2f);
}
(_, GaiaType::Object) | (_, GaiaType::String) => {
instructions.push(JvmInstruction::Checkcast { class_name: to.to_string().replace('.', "/") });
}
_ => {
}
}
Ok(instructions)
},
CoreInstruction::And(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Iand,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Land,
_ => JvmInstruction::Iand,
}]),
CoreInstruction::Or(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Ior,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lor,
_ => JvmInstruction::Ior,
}]),
CoreInstruction::Xor(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Ixor,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lxor,
_ => JvmInstruction::Ixor,
}]),
CoreInstruction::Shl(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Ishl,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lshl,
_ => JvmInstruction::Ishl,
}]),
CoreInstruction::Shr(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Ishr,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lshr,
_ => JvmInstruction::Ishr,
}]),
CoreInstruction::Neg(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Ineg,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lneg,
GaiaType::F32 => JvmInstruction::Fneg,
GaiaType::F64 => JvmInstruction::Dneg,
_ => JvmInstruction::Ineg,
}]),
CoreInstruction::Not(ty) => Ok(vec![match ty {
GaiaType::I32 | GaiaType::U32 | GaiaType::Bool | GaiaType::I8 | GaiaType::U8 | GaiaType::I16 | GaiaType::U16 => JvmInstruction::Ixor,
GaiaType::I64 | GaiaType::U64 => JvmInstruction::Lxor,
_ => JvmInstruction::Ixor,
}]),
CoreInstruction::CallIndirect(arg_count) => Err(GaiaError::not_implemented("JVM CallIndirect")),
CoreInstruction::Alloca(ty, count) => {
Ok(vec![])
},
CoreInstruction::Throw => Ok(vec![JvmInstruction::Athrow]),
_ => Ok(vec![]),
}
},
_ => Ok(vec![]),
}
}
#[cfg(feature = "jvm-assembler")]
fn convert_gaia_type_to_jvm_descriptor(ty: &GaiaType) -> String {
mapping::map_gaia_type_to_jvm_descriptor(ty)
}
#[cfg(feature = "jvm-assembler")]
fn calculate_stack_and_locals(function: &GaiaFunction, _ctx: &JvmContext) -> Result<(u16, u16)> {
let mut max_stack: i32 = 0;
let mut current_stack: i32 = 0;
let mut max_locals: u16 = 0;
for (i, param) in function.signature.params.iter().enumerate() {
let size = match param {
GaiaType::I64 | GaiaType::U64 | GaiaType::F64 => 2u16,
_ => 1u16,
};
max_locals += size;
}
for block in &function.blocks {
for instruction in &block.instructions {
match instruction {
GaiaInstruction::Core(core) => match core {
CoreInstruction::PushConstant(constant) => {
match constant {
GaiaConstant::I64(_) | GaiaConstant::U64(_) | GaiaConstant::F64(_) => {
current_stack += 2;
}
_ => {
current_stack += 1;
}
}
if current_stack > max_stack {
max_stack = current_stack;
}
}
CoreInstruction::Add(ty) | CoreInstruction::Sub(ty) | CoreInstruction::Mul(ty) | CoreInstruction::Div(ty) | CoreInstruction::Rem(ty) => {
match ty {
GaiaType::I64 | GaiaType::U64 | GaiaType::F64 => {
current_stack -= 1; }
_ => {
current_stack -= 1; }
}
}
CoreInstruction::Pop => {
current_stack -= 1;
}
CoreInstruction::Dup => {
current_stack += 1;
if current_stack > max_stack {
max_stack = current_stack;
}
}
CoreInstruction::LoadLocal(_, ty) | CoreInstruction::LoadArg(_, ty) => {
match ty {
GaiaType::I64 | GaiaType::U64 | GaiaType::F64 => {
current_stack += 2;
}
_ => {
current_stack += 1;
}
}
if current_stack > max_stack {
max_stack = current_stack;
}
}
CoreInstruction::StoreLocal(index, ty) | CoreInstruction::StoreArg(index, ty) => {
match ty {
GaiaType::I64 | GaiaType::U64 | GaiaType::F64 => {
current_stack -= 2;
let local_index = *index as u16;
if local_index + 1 > max_locals {
max_locals = local_index + 2;
}
}
_ => {
current_stack -= 1;
let local_index = *index as u16;
if local_index > max_locals {
max_locals = local_index + 1;
}
}
}
}
CoreInstruction::New(_) => {
current_stack += 1; if current_stack > max_stack {
max_stack = current_stack;
}
}
CoreInstruction::LoadField(_, _) => {
current_stack += 1; if current_stack > max_stack {
max_stack = current_stack;
}
}
CoreInstruction::StoreField(_, _) => {
current_stack -= 1; }
CoreInstruction::LoadElement(ty) => {
match ty {
GaiaType::I64 | GaiaType::U64 | GaiaType::F64 => {
current_stack += 1; }
_ => {
current_stack += 1; }
}
if current_stack > max_stack {
max_stack = current_stack;
}
}
CoreInstruction::StoreElement(ty) => {
match ty {
GaiaType::I64 | GaiaType::U64 | GaiaType::F64 => {
current_stack -= 2; }
_ => {
current_stack -= 2; }
}
}
CoreInstruction::Call(_, arg_count) => {
current_stack -= *arg_count as i32;
current_stack += 1;
if current_stack > max_stack {
max_stack = current_stack;
}
}
CoreInstruction::NewArray(_, _) => {
current_stack -= 1; current_stack += 1; }
CoreInstruction::ArrayLength => {
current_stack -= 1; current_stack += 1; }
CoreInstruction::ArrayPush => {
current_stack -= 2; }
CoreInstruction::Throw => {
current_stack -= 1;
}
CoreInstruction::Br(_) | CoreInstruction::BrTrue(_) | CoreInstruction::BrFalse(_) | CoreInstruction::Label(_) => {
}
CoreInstruction::Ret => {
current_stack = 0;
}
_ => {
}
},
_ => {
}
}
}
}
if max_stack < 0 {
max_stack = 0;
}
max_stack = std::cmp::max(max_stack, 4);
max_locals = std::cmp::max(max_locals, 4u16);
Ok((max_stack as u16, max_locals as u16))
}
#[cfg(feature = "jvm-assembler")]
fn build_method_descriptor(params: &[GaiaType], return_type: &Option<GaiaType>) -> String {
let mut descriptor = "(".to_string();
for param in params {
descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(param));
}
descriptor.push(')');
if let Some(ret) = return_type {
descriptor.push_str(&convert_gaia_type_to_jvm_descriptor(ret));
}
else {
descriptor.push('V');
}
descriptor
}