mirror of
https://github.com/fluencelabs/asmble
synced 2025-04-24 22:32:19 +00:00
Early completion of all ops
This commit is contained in:
parent
f13835ca8c
commit
8afc6ca9cd
@ -2,6 +2,7 @@ package asmble.compile.jvm
|
||||
|
||||
import asmble.ast.Node
|
||||
import asmble.util.Either
|
||||
import asmble.util.add
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.*
|
||||
import java.lang.invoke.MethodHandle
|
||||
@ -184,7 +185,28 @@ open class AstToAsm {
|
||||
fn.addInsns(UnsupportedOperationException::class.athrow("Unreachable"))
|
||||
is Node.Instr.Nop ->
|
||||
fn.addInsns(InsnNode(Opcodes.NOP))
|
||||
// TODO: other control flow...
|
||||
is Node.Instr.Block ->
|
||||
// TODO: check last item on stack?
|
||||
fn.pushBlock(i)
|
||||
is Node.Instr.Loop ->
|
||||
fn.pushBlock(i)
|
||||
is Node.Instr.If ->
|
||||
// The label is set in else or end
|
||||
fn.pushBlock(i).pushIf().addInsns(JumpInsnNode(Opcodes.IFEQ, null))
|
||||
is Node.Instr.Else ->
|
||||
applyElse(ctx, fn)
|
||||
is Node.Instr.End ->
|
||||
applyEnd(ctx, fn)
|
||||
is Node.Instr.Br ->
|
||||
fn.blockAtDepth(i.relativeDepth).let { (fn, block) ->
|
||||
fn.addInsns(JumpInsnNode(Opcodes.GOTO, block.label))
|
||||
}
|
||||
is Node.Instr.BrIf ->
|
||||
fn.blockAtDepth(i.relativeDepth).let { (fn, block) ->
|
||||
fn.addInsns(JumpInsnNode(Opcodes.IFNE, block.label))
|
||||
}
|
||||
is Node.Instr.BrTable ->
|
||||
applyBrTable(ctx, fn, i)
|
||||
is Node.Instr.Return ->
|
||||
applyReturnInsn(ctx, fn)
|
||||
is Node.Instr.Call ->
|
||||
@ -297,9 +319,350 @@ open class AstToAsm {
|
||||
applyF64Cmp(ctx, fn, Opcodes.IFLE)
|
||||
is Node.Instr.F64Ge ->
|
||||
applyF64Cmp(ctx, fn, Opcodes.IFGE)
|
||||
else -> TODO()
|
||||
is Node.Instr.I32Clz ->
|
||||
// TODO Should make unsigned?
|
||||
applyI32Unary(ctx, fn, Integer::numberOfLeadingZeros.invokeStatic())
|
||||
is Node.Instr.I32Ctz ->
|
||||
applyI32Unary(ctx, fn, Integer::numberOfTrailingZeros.invokeStatic())
|
||||
is Node.Instr.I32Popcnt ->
|
||||
applyI32Unary(ctx, fn, Integer::bitCount.invokeStatic())
|
||||
is Node.Instr.I32Add ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IADD)
|
||||
is Node.Instr.I32Sub ->
|
||||
applyI32Binary(ctx, fn, Opcodes.ISUB)
|
||||
is Node.Instr.I32Mul ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IMUL)
|
||||
is Node.Instr.I32DivS ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IDIV)
|
||||
is Node.Instr.I32DivU ->
|
||||
applyI32Binary(ctx, fn, Integer::divideUnsigned.invokeStatic())
|
||||
is Node.Instr.I32RemS ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IREM)
|
||||
is Node.Instr.I32RemU ->
|
||||
applyI32Binary(ctx, fn, Integer::remainderUnsigned.invokeStatic())
|
||||
is Node.Instr.I32And ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IAND)
|
||||
is Node.Instr.I32Or ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IOR)
|
||||
is Node.Instr.I32Xor ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IXOR)
|
||||
is Node.Instr.I32Shl ->
|
||||
applyI32Binary(ctx, fn, Opcodes.ISHL)
|
||||
is Node.Instr.I32ShrS ->
|
||||
applyI32Binary(ctx, fn, Opcodes.ISHR)
|
||||
is Node.Instr.I32ShrU ->
|
||||
applyI32Binary(ctx, fn, Opcodes.IUSHR)
|
||||
is Node.Instr.I32Rotl ->
|
||||
applyI32Binary(ctx, fn, Integer::rotateLeft.invokeStatic())
|
||||
is Node.Instr.I32Rotr ->
|
||||
applyI32Binary(ctx, fn, Integer::rotateRight.invokeStatic())
|
||||
is Node.Instr.I64Clz ->
|
||||
applyI64Unary(ctx, fn, java.lang.Long::numberOfLeadingZeros.invokeStatic())
|
||||
is Node.Instr.I64Ctz ->
|
||||
applyI64Unary(ctx, fn, java.lang.Long::numberOfTrailingZeros.invokeStatic())
|
||||
is Node.Instr.I64Popcnt ->
|
||||
applyI64Unary(ctx, fn, java.lang.Long::bitCount.invokeStatic())
|
||||
is Node.Instr.I64Add ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LADD)
|
||||
is Node.Instr.I64Sub ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LSUB)
|
||||
is Node.Instr.I64Mul ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LMUL)
|
||||
is Node.Instr.I64DivS ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LDIV)
|
||||
is Node.Instr.I64DivU ->
|
||||
applyI64Binary(ctx, fn, java.lang.Long::divideUnsigned.invokeStatic())
|
||||
is Node.Instr.I64RemS ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LREM)
|
||||
is Node.Instr.I64RemU ->
|
||||
applyI64Binary(ctx, fn, java.lang.Long::remainderUnsigned.invokeStatic())
|
||||
is Node.Instr.I64And ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LAND)
|
||||
is Node.Instr.I64Or ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LOR)
|
||||
is Node.Instr.I64Xor ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LXOR)
|
||||
is Node.Instr.I64Shl ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LSHL)
|
||||
is Node.Instr.I64ShrS ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LSHR)
|
||||
is Node.Instr.I64ShrU ->
|
||||
applyI64Binary(ctx, fn, Opcodes.LUSHR)
|
||||
is Node.Instr.I64Rotl ->
|
||||
applyI64Binary(ctx, fn, java.lang.Long::rotateLeft.invokeStatic())
|
||||
is Node.Instr.I64Rotr ->
|
||||
applyI64Binary(ctx, fn, java.lang.Long::rotateRight.invokeStatic())
|
||||
is Node.Instr.F32Abs ->
|
||||
applyF32Unary(ctx, fn, forceFnType<(Float) -> Float>(Math::abs).invokeStatic())
|
||||
is Node.Instr.F32Neg ->
|
||||
applyF32Unary(ctx, fn, InsnNode(Opcodes.FNEG))
|
||||
is Node.Instr.F32Ceil ->
|
||||
applyWithF32To64AndBack(ctx, fn) { applyF64Unary(ctx, it, Math::ceil.invokeStatic()) }
|
||||
is Node.Instr.F32Floor ->
|
||||
applyWithF32To64AndBack(ctx, fn) { applyF64Unary(ctx, it, Math::floor.invokeStatic()) }
|
||||
is Node.Instr.F32Trunc ->
|
||||
applyF32Trunc(ctx, fn)
|
||||
is Node.Instr.F32Nearest ->
|
||||
// TODO: this ain't right wrt infinity and other things
|
||||
applyF32Unary(ctx, fn, forceFnType<(Float) -> Int>(Math::round).invokeStatic()).
|
||||
addInsns(InsnNode(Opcodes.I2F))
|
||||
is Node.Instr.F32Sqrt ->
|
||||
applyWithF32To64AndBack(ctx, fn) { applyF64Unary(ctx, it, Math::sqrt.invokeStatic()) }
|
||||
is Node.Instr.F32Add ->
|
||||
applyF32Binary(ctx, fn, Opcodes.FADD)
|
||||
is Node.Instr.F32Sub ->
|
||||
applyF32Binary(ctx, fn, Opcodes.FSUB)
|
||||
is Node.Instr.F32Mul ->
|
||||
applyF32Binary(ctx, fn, Opcodes.FMUL)
|
||||
is Node.Instr.F32Div ->
|
||||
applyF32Binary(ctx, fn, Opcodes.FDIV)
|
||||
is Node.Instr.F32Min ->
|
||||
applyF32Binary(ctx, fn, forceFnType<(Float, Float) -> Float>(Math::min).invokeStatic())
|
||||
is Node.Instr.F32Max ->
|
||||
applyF32Binary(ctx, fn, forceFnType<(Float, Float) -> Float>(Math::max).invokeStatic())
|
||||
is Node.Instr.F32CopySign ->
|
||||
applyF32Binary(ctx, fn, forceFnType<(Float, Float) -> Float>(Math::copySign).invokeStatic())
|
||||
is Node.Instr.F64Abs ->
|
||||
applyF64Unary(ctx, fn, forceFnType<(Double) -> Double>(Math::abs).invokeStatic())
|
||||
is Node.Instr.F64Neg ->
|
||||
applyF64Unary(ctx, fn, InsnNode(Opcodes.DNEG))
|
||||
is Node.Instr.F64Ceil ->
|
||||
applyF64Unary(ctx, fn, Math::ceil.invokeStatic())
|
||||
is Node.Instr.F64Floor ->
|
||||
applyF64Unary(ctx, fn, Math::floor.invokeStatic())
|
||||
is Node.Instr.F64Trunc ->
|
||||
applyF64Trunc(ctx, fn)
|
||||
is Node.Instr.F64Nearest ->
|
||||
// TODO: this ain't right wrt infinity and other things
|
||||
applyF64Unary(ctx, fn, forceFnType<(Double) -> Long>(Math::round).invokeStatic()).
|
||||
addInsns(InsnNode(Opcodes.L2D))
|
||||
is Node.Instr.F64Sqrt ->
|
||||
applyF64Unary(ctx, fn, Math::sqrt.invokeStatic())
|
||||
is Node.Instr.F64Add ->
|
||||
applyF64Binary(ctx, fn, Opcodes.DADD)
|
||||
is Node.Instr.F64Sub ->
|
||||
applyF64Binary(ctx, fn, Opcodes.DSUB)
|
||||
is Node.Instr.F64Mul ->
|
||||
applyF64Binary(ctx, fn, Opcodes.DMUL)
|
||||
is Node.Instr.F64Div ->
|
||||
applyF64Binary(ctx, fn, Opcodes.DDIV)
|
||||
is Node.Instr.F64Min ->
|
||||
applyF64Binary(ctx, fn, forceFnType<(Double, Double) -> Double>(Math::min).invokeStatic())
|
||||
is Node.Instr.F64Max ->
|
||||
applyF64Binary(ctx, fn, forceFnType<(Double, Double) -> Double>(Math::max).invokeStatic())
|
||||
is Node.Instr.F64CopySign ->
|
||||
applyF64Binary(ctx, fn, forceFnType<(Double, Double) -> Double>(Math::copySign).invokeStatic())
|
||||
is Node.Instr.I32WrapI64 ->
|
||||
applyConv(ctx, fn, Long::class.ref, Int::class.ref, Opcodes.L2I)
|
||||
is Node.Instr.I32TruncSF32 ->
|
||||
applyConv(ctx, fn, Float::class.ref, Int::class.ref, Opcodes.F2I)
|
||||
is Node.Instr.I32TruncUF32 ->
|
||||
// TODO: wat?
|
||||
applyConv(ctx, fn, Float::class.ref, Int::class.ref, Opcodes.F2I).
|
||||
addInsns(java.lang.Short::toUnsignedInt.invokeStatic())
|
||||
is Node.Instr.I32TruncSF64 ->
|
||||
applyConv(ctx, fn, Double::class.ref, Int::class.ref, Opcodes.D2I)
|
||||
is Node.Instr.I32TruncUF64 ->
|
||||
applyConv(ctx, fn, Double::class.ref, Int::class.ref, Opcodes.D2I).
|
||||
addInsns(java.lang.Short::toUnsignedInt.invokeStatic())
|
||||
is Node.Instr.I64ExtendSI32 ->
|
||||
applyConv(ctx, fn, Int::class.ref, Long::class.ref, Opcodes.I2L)
|
||||
is Node.Instr.I64ExtendUI32 ->
|
||||
applyConv(ctx, fn, Int::class.ref, Long::class.ref, Integer::toUnsignedLong.invokeStatic())
|
||||
is Node.Instr.I64TruncSF32 ->
|
||||
applyConv(ctx, fn, Float::class.ref, Long::class.ref, Opcodes.F2L)
|
||||
is Node.Instr.I64TruncUF32 ->
|
||||
applyConv(ctx, fn, Float::class.ref, Long::class.ref, Opcodes.F2L).
|
||||
addInsns(0xffL.const, InsnNode(Opcodes.LAND))
|
||||
is Node.Instr.I64TruncSF64 ->
|
||||
applyConv(ctx, fn, Double::class.ref, Long::class.ref, Opcodes.D2L)
|
||||
is Node.Instr.I64TruncUF64 ->
|
||||
// TODO: so wrong
|
||||
applyConv(ctx, fn, Double::class.ref, Long::class.ref, Opcodes.D2L).
|
||||
addInsns(0xffL.const, InsnNode(Opcodes.LAND))
|
||||
is Node.Instr.F32ConvertSI32 ->
|
||||
applyConv(ctx, fn, Int::class.ref, Float::class.ref, Opcodes.I2F)
|
||||
is Node.Instr.F32ConvertUI32 ->
|
||||
fn.addInsns(Integer::toUnsignedLong.invokeStatic()).
|
||||
let { applyConv(ctx, it, Int::class.ref, Float::class.ref, Opcodes.L2F) }
|
||||
is Node.Instr.F32ConvertSI64 ->
|
||||
applyConv(ctx, fn, Long::class.ref, Float::class.ref, Opcodes.L2F)
|
||||
is Node.Instr.F32ConvertUI64 ->
|
||||
fn.addInsns(0xffL.const, InsnNode(Opcodes.LAND)).
|
||||
let { applyConv(ctx, it, Long::class.ref, Float::class.ref, Opcodes.L2F) }
|
||||
is Node.Instr.F32DemoteF64 ->
|
||||
applyConv(ctx, fn, Double::class.ref, Float::class.ref, Opcodes.D2F)
|
||||
is Node.Instr.F64ConvertSI32 ->
|
||||
applyConv(ctx, fn, Int::class.ref, Double::class.ref, Opcodes.I2D)
|
||||
is Node.Instr.F64ConvertUI32 ->
|
||||
fn.addInsns(Integer::toUnsignedLong.invokeStatic()).
|
||||
let { applyConv(ctx, it, Int::class.ref, Double::class.ref, Opcodes.L2D) }
|
||||
is Node.Instr.F64ConvertSI64 ->
|
||||
applyConv(ctx, fn, Long::class.ref, Double::class.ref, Opcodes.L2D)
|
||||
is Node.Instr.F64ConvertUI64 ->
|
||||
fn.addInsns(0xffL.const, InsnNode(Opcodes.LAND)).
|
||||
let { applyConv(ctx, it, Long::class.ref, Double::class.ref, Opcodes.L2D) }
|
||||
is Node.Instr.F64PromoteF32 ->
|
||||
applyConv(ctx, fn, Float::class.ref, Double::class.ref, Opcodes.F2D)
|
||||
is Node.Instr.I32ReinterpretF32 ->
|
||||
applyConv(ctx, fn, Float::class.ref, Int::class.ref, java.lang.Float::floatToRawIntBits.invokeStatic())
|
||||
is Node.Instr.I64ReinterpretF64 ->
|
||||
applyConv(ctx, fn, Double::class.ref, Long::class.ref, java.lang.Double::doubleToRawLongBits.invokeStatic())
|
||||
is Node.Instr.F32ReinterpretI32 ->
|
||||
applyConv(ctx, fn, Int::class.ref, Float::class.ref, java.lang.Float::intBitsToFloat.invokeStatic())
|
||||
is Node.Instr.F64ReinterpretI64 ->
|
||||
applyConv(ctx, fn, Double::class.ref, Long::class.ref, java.lang.Double::longBitsToDouble.invokeStatic())
|
||||
}
|
||||
|
||||
// Can compile quite cleanly as a table switch on the JVM
|
||||
fun applyBrTable(ctx: FuncContext, fn: Func, insn: Node.Instr.BrTable) =
|
||||
fn.blockAtDepth(insn.default).let { (fn, defaultBlock) ->
|
||||
insn.targetTable.fold(fn to emptyList<LabelNode>()) { (fn, labels), targetDepth ->
|
||||
fn.blockAtDepth(targetDepth).let { (fn, targetBlock) -> fn to (labels + targetBlock.label) }
|
||||
}.let { (fn, targetLabels) ->
|
||||
fn.addInsns(TableSwitchInsnNode(0, targetLabels.size - 1,
|
||||
defaultBlock.label, *targetLabels.toTypedArray()))
|
||||
}
|
||||
}
|
||||
|
||||
fun applyElse(ctx: FuncContext, fn: Func) = fn.blockAtDepth(0).let { (fn, block) ->
|
||||
// Do a goto the end, and then add a fresh label to the initial "if" that jumps here
|
||||
val label = LabelNode()
|
||||
fn.peekIf().label = label
|
||||
fn.addInsns(JumpInsnNode(Opcodes.GOTO, block.label), label)
|
||||
}
|
||||
|
||||
fun applyEnd(ctx: FuncContext, fn: Func) = fn.popBlock().let { (fn, block) ->
|
||||
when (block.insn) {
|
||||
is Node.Instr.Block ->
|
||||
// Add label to end of block if it's there
|
||||
block.maybeLabel?.let { fn.addInsns(it) } ?: fn
|
||||
is Node.Instr.Loop ->
|
||||
// Add label to beginning of loop if it's there
|
||||
block.maybeLabel?.let { fn.copy(insns = fn.insns.add(block.startIndex, it)) } ?: fn
|
||||
is Node.Instr.If -> fn.popIf().let { (fn, jumpNode) ->
|
||||
when (block.maybeLabel) {
|
||||
// If there is no existing break label, add one to initial
|
||||
// "if" only if it isn't there from an "else"
|
||||
null -> if (jumpNode.label != null) fn else {
|
||||
jumpNode.label = LabelNode()
|
||||
fn.addInsns(jumpNode.label)
|
||||
}
|
||||
// If there is one, add it to the initial "if"
|
||||
// if the "else" didn't set one on there...then push it
|
||||
else -> {
|
||||
if (jumpNode.label == null) jumpNode.label = block.maybeLabel
|
||||
fn.addInsns(block.maybeLabel!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> error("Unrecognized end for ${block.insn}")
|
||||
}
|
||||
}
|
||||
|
||||
fun applyConv(ctx: FuncContext, fn: Func, from: TypeRef, to: TypeRef, op: Int) =
|
||||
applyConv(ctx, fn, from, to, InsnNode(op))
|
||||
|
||||
fun applyConv(ctx: FuncContext, fn: Func, from: TypeRef, to: TypeRef, insn: AbstractInsnNode) =
|
||||
fn.popExpecting(from).addInsns(insn).push(to)
|
||||
|
||||
|
||||
fun applyF64Trunc(ctx: FuncContext, fn: Func): Func {
|
||||
// The best way for now is a comparison and jump to ceil or floor sadly
|
||||
// So with it on the stack:
|
||||
// dup2
|
||||
// dconst 0
|
||||
// dcmpg
|
||||
// ifge label1
|
||||
// Math::ceil
|
||||
// goto label2
|
||||
// label1: Math::floor
|
||||
// label2
|
||||
val label1 = LabelNode()
|
||||
val label2 = LabelNode()
|
||||
return fn.popExpecting(Double::class.ref).addInsns(
|
||||
InsnNode(Opcodes.DUP2),
|
||||
0.0.const,
|
||||
InsnNode(Opcodes.DCMPG),
|
||||
JumpInsnNode(Opcodes.IFGE, label1),
|
||||
Math::ceil.invokeStatic(),
|
||||
JumpInsnNode(Opcodes.GOTO, label2),
|
||||
label1,
|
||||
Math::floor.invokeStatic(),
|
||||
label2
|
||||
).push(Double::class.ref)
|
||||
}
|
||||
|
||||
fun applyF32Trunc(ctx: FuncContext, fn: Func): Func {
|
||||
// Do the same as applyF64Trunc but convert where needed
|
||||
val label1 = LabelNode()
|
||||
val label2 = LabelNode()
|
||||
return fn.popExpecting(Float::class.ref).addInsns(
|
||||
InsnNode(Opcodes.DUP),
|
||||
0.0F.const,
|
||||
InsnNode(Opcodes.FCMPG),
|
||||
JumpInsnNode(Opcodes.IFGE, label1),
|
||||
InsnNode(Opcodes.F2D),
|
||||
Math::ceil.invokeStatic(),
|
||||
JumpInsnNode(Opcodes.GOTO, label2),
|
||||
label1,
|
||||
InsnNode(Opcodes.F2D),
|
||||
Math::floor.invokeStatic(),
|
||||
label2,
|
||||
InsnNode(Opcodes.D2F)
|
||||
).push(Float::class.ref)
|
||||
}
|
||||
|
||||
fun applyWithF32To64AndBack(ctx: FuncContext, fn: Func, f: (Func) -> Func) =
|
||||
fn.popExpecting(Float::class.ref).
|
||||
addInsns(InsnNode(Opcodes.F2D)).
|
||||
push(Double::class.ref).let(f).
|
||||
popExpecting(Double::class.ref).
|
||||
addInsns(InsnNode(Opcodes.D2F)).
|
||||
push(Float::class.ref)
|
||||
|
||||
fun applyF64Binary(ctx: FuncContext, fn: Func, op: Int) =
|
||||
applyF64Binary(ctx, fn, InsnNode(op))
|
||||
|
||||
fun applyF64Binary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyBinary(ctx, fn, Double::class.ref, insn)
|
||||
|
||||
fun applyF32Binary(ctx: FuncContext, fn: Func, op: Int) =
|
||||
applyF32Binary(ctx, fn, InsnNode(op))
|
||||
|
||||
fun applyF32Binary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyBinary(ctx, fn, Float::class.ref, insn)
|
||||
|
||||
fun applyI64Binary(ctx: FuncContext, fn: Func, op: Int) =
|
||||
applyI64Binary(ctx, fn, InsnNode(op))
|
||||
|
||||
fun applyI64Binary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyBinary(ctx, fn, Long::class.ref, insn)
|
||||
|
||||
fun applyI32Binary(ctx: FuncContext, fn: Func, op: Int) =
|
||||
applyI32Binary(ctx, fn, InsnNode(op))
|
||||
|
||||
fun applyI32Binary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyBinary(ctx, fn, Int::class.ref, insn)
|
||||
|
||||
fun applyBinary(ctx: FuncContext, fn: Func, type: TypeRef, insn: AbstractInsnNode) =
|
||||
fn.popExpectingMulti(type, type).addInsns(insn).push(type)
|
||||
|
||||
fun applyF64Unary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyUnary(ctx, fn, Double::class.ref, insn)
|
||||
|
||||
fun applyF32Unary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyUnary(ctx, fn, Float::class.ref, insn)
|
||||
|
||||
fun applyI64Unary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyUnary(ctx, fn, Long::class.ref, insn)
|
||||
|
||||
fun applyI32Unary(ctx: FuncContext, fn: Func, insn: AbstractInsnNode) =
|
||||
applyUnary(ctx, fn, Int::class.ref, insn)
|
||||
|
||||
fun applyUnary(ctx: FuncContext, fn: Func, type: TypeRef, insn: AbstractInsnNode) =
|
||||
fn.popExpecting(type).addInsns(insn).push(type)
|
||||
|
||||
fun applyF32Cmp(ctx: FuncContext, fn: Func, op: Int) =
|
||||
fn.popExpecting(Float::class.ref).
|
||||
popExpecting(Float::class.ref).
|
||||
|
@ -2,10 +2,7 @@ package asmble.compile.jvm
|
||||
|
||||
import asmble.ast.Node
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
import org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.objectweb.asm.tree.InsnNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
import org.objectweb.asm.tree.*
|
||||
|
||||
data class Func(
|
||||
val name: String,
|
||||
@ -14,7 +11,10 @@ data class Func(
|
||||
val access: Int = Opcodes.ACC_PUBLIC,
|
||||
val insns: List<AbstractInsnNode> = emptyList(),
|
||||
val stack: List<TypeRef> = emptyList(),
|
||||
val memIsLocalVar: Boolean = false
|
||||
val memIsLocalVar: Boolean = false,
|
||||
val blockStack: List<Block> = emptyList(),
|
||||
// Contains index of JumpInsnNode that has a null label
|
||||
val ifStack: List<Int> = emptyList()
|
||||
) {
|
||||
|
||||
val desc: String get() = ret.asMethodRetDesc(*params.toTypedArray())
|
||||
@ -74,4 +74,48 @@ data class Func(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun pushBlock(insn: Node.Instr) = copy(blockStack = blockStack + Block.NoLabel(insn, insns.size))
|
||||
|
||||
fun popBlock() = copy(blockStack = blockStack.dropLast(1)) to blockStack.last()
|
||||
|
||||
fun blockAtDepth(depth: Int) = blockStack[blockStack.size - depth].let { block ->
|
||||
when (block) {
|
||||
// We have to lazily create it here
|
||||
is Block.NoLabel -> blockStack.toMutableList().let {
|
||||
val newBlock = block.withLabel(LabelNode())
|
||||
it[blockStack.size - depth] = newBlock
|
||||
copy(blockStack = it) to newBlock
|
||||
}
|
||||
is Block.WithLabel -> this to block
|
||||
}
|
||||
}
|
||||
|
||||
fun pushIf() = copy(ifStack = ifStack + insns.size)
|
||||
|
||||
fun peekIf() = insns[ifStack.last()] as JumpInsnNode
|
||||
|
||||
fun popIf() = copy(ifStack = ifStack.dropLast(1)) to peekIf()
|
||||
|
||||
sealed class Block {
|
||||
abstract val insn: Node.Instr
|
||||
abstract val startIndex: Int
|
||||
abstract val maybeLabel: LabelNode?
|
||||
|
||||
data class NoLabel(
|
||||
override val insn: Node.Instr,
|
||||
override val startIndex: Int
|
||||
) : Block() {
|
||||
override val maybeLabel: LabelNode? get() = null
|
||||
fun withLabel(label: LabelNode) = WithLabel(insn, startIndex, label)
|
||||
}
|
||||
|
||||
data class WithLabel(
|
||||
override val insn: Node.Instr,
|
||||
override val startIndex: Int,
|
||||
val label: LabelNode
|
||||
) : Block() {
|
||||
override val maybeLabel: LabelNode? get() = label
|
||||
}
|
||||
}
|
||||
}
|
6
src/main/kotlin/asmble/util/ListExt.kt
Normal file
6
src/main/kotlin/asmble/util/ListExt.kt
Normal file
@ -0,0 +1,6 @@
|
||||
package asmble.util
|
||||
|
||||
fun <T : Any> List<T>.add(index: Int, v: T): List<T> = this.toMutableList().let {
|
||||
it.add(index, v)
|
||||
it
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user