From 56ce498ef9e3ec33209eaed1393fce172d21a820 Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Sat, 11 Jul 2026 14:22:50 +0200 Subject: [PATCH 01/11] wip: Float support and rv64f.vadl # Conflicts: # vadl-frontend/main/vadl/ast/TypeChecker.java --- sys/risc-v/rv64f.vadl | 363 ++++++++++++++++++ vadl-frontend/main/vadl/ast/TypeChecker.java | 22 ++ vadl/main/vadl/types/BuiltInTable.java | 30 ++ vadl/main/vadl/types/FloatStatusType.java | 54 +++ vadl/main/vadl/types/FloatType.java | 67 ++++ vadl/main/vadl/types/Type.java | 77 +++- .../VadlBuiltInFloatStatusOnlyDispatcher.java | 45 +++ .../StatusBuiltInInlinePass.java | 38 +- 8 files changed, 692 insertions(+), 4 deletions(-) create mode 100644 sys/risc-v/rv64f.vadl create mode 100644 vadl/main/vadl/types/FloatStatusType.java create mode 100644 vadl/main/vadl/types/FloatType.java create mode 100644 vadl/main/vadl/utils/VadlBuiltInFloatStatusOnlyDispatcher.java diff --git a/sys/risc-v/rv64f.vadl b/sys/risc-v/rv64f.vadl new file mode 100644 index 000000000..391555da8 --- /dev/null +++ b/sys/risc-v/rv64f.vadl @@ -0,0 +1,363 @@ + +import rv64csr::{RV64IMZicsr} + +instruction set architecture RV64IMF extending RV64IMZicsr = { + + model FSize() : Id = {FSize32} + + using ConstTy = UInt<8> + constant FLEN : ConstTy = $FSize + constant FSize32 : ConstTy = 32 + constant FSize64 : ConstTy = 64 + constant FSize128 : ConstTy = 128 + + // TODO: these need to be built-in types + using FP16 = Bits<16> + using FP128 = Bits<128> + + using SIntH = SInt<16> + using UIntH = UInt<16> + using SIntD = SInt<64> + using UIntD = UInt<64> + using SIntQ = SInt<128> + using UIntQ = UInt<128> + + using FRegs = Bits + + using Bits2 = Bits<2> + + register F : Index -> FRegs + + register FCSR : FCsrFormat + + format FCsrFormat : Bits<32> = + { reserved [31..8] + , frm [7..5] // Rounding mode + , fflags [4..0] // Float exception flags + , nv = fflags(4) // Float exception flag: Invalid operation + , dz = fflags(3) // Float exception flag: Division by zero + , of = fflags(2) // Float exception flag: Overflow + , uf = fflags(1) // Float exception flag: Underflow + , nx = fflags(0) // Float exception flag: Inexact + } + + // rounding modes + enumeration Frm : Bits3 = + { rne = 0b000 // Round to Nearest, ties to Even + , rtz = 0b001 // Round to Zero + , rdn = 0b010 // Round Down (towards -infinity) + , rup = 0b011 // Round UP (towards infinity) + , rmm = 0b100 // Round to Nearest, ties to Max Magnitude + // 0b101 // reserved in FRtype.funct3 + // 0b110 // reserved in FRtype.funct3 + , dyn = 0b111 // reserved in FRtype.funct3; in instruction: Dynamic rounding (uses FRtype.funct3) + } + + // format (precision) + enumeration Fmt : Bits2 = + { s = 0b00 // single precision (32 -bit) + , d = 0b01 // double precision (64 -bit) + , h = 0b10 // half precision (16 -bit) + , q = 0b11 // quad precision (128-bit) + } + + function FrmName(frm : Bits3) -> String = + match frm with + { Frm::rne => "rne" + , Frm::rtz => "rtz" + , Frm::rdn => "rdn" + , Frm::rup => "rup" + , Frm::rmm => "rmm" + , _ => "" + } + + format FRtype : Inst = // Rtype register 3 operand instruction format (for float ops) + { funct5 : Bits5 // [31..27] 5 bit function code + , fmt : Bits2 // [26..25] 2 bit format code + , rs2 : Index // [24..20] 2nd source register index / shamt + , rs1 : Index // [19..15] 1st source register index + , funct3 : Bits3 // [14..12] 3 bit function code + , rd : Index // [11..7] destination register index + , opcode : Bits7 // [6..0] 7 bit operation code + , shamt = rs2 as UInt // 5 bit unsigned shift ammount + } + + format R4type : Inst = // R4type register 4 operand instruction format + { rs3 : Bits5 // [31..27] 3rd source register index + , fmt : Bits2 // [26..25] 2 bit format + , rs2 : Index // [24..20] 2nd source register index + , rs1 : Index // [19..15] 1st source register index + , funct3 : Bits3 // [14..12] 3 bit function code + , rd : Index // [11..7] destination register index + , opcode : Bits7 // [6..0] 7 bit operation code + } + + record FRtypeRec (name : Id, mne : Str, rm : Ex, fmt : Ex, funct5 : Bin, instr : Stat) + + model FRtypeInstr (c : FRtypeRec, enc : Encs, asm : IsaDefs) : IsaDefs = { + instruction $c.name : FRtype = $c.instr + encoding $c.name = {opcode = 0b101'0011, funct3 = $c.rm, fmt = $c.fmt, funct5 = $c.funct5, $enc} + $asm + } + + model FRtypeInstr1 (c : FRtypeRec, rs2 : Bin) : IsaDefs = { + $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1))) + } + + model FRtypeInstr1rm (c : FRtypeRec, rs2 : Bin) : IsaDefs = { + $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", FrmName($c.rm))) + } + + model FRtypeInstr2 (c : FRtypeRec) : IsaDefs = { + $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2))) + } + + model FRtypeInstr2rm (c : FRtypeRec) : IsaDefs = { + $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($c.rm))) + } + + record FSizeRec (suffix : Id, iSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, size : Int) + + model FSize16 () : FSizeRec = {(H ; H ; Fmt::h ; FP16 ; SIntH ; UIntH ; 16 )} + model FSize32 () : FSizeRec = {(S ; W ; Fmt::s ; FP32 ; SIntW ; UIntW ; 32 )} + model FSize64 () : FSizeRec = {(D ; D ; Fmt::d ; FP64 ; SIntD ; UIntD ; 64 )} + model FSize128 () : FSizeRec = {(Q ; Q ; Fmt::q ; FP128 ; SIntQ ; UIntQ ; 128)} + + function NaNBoxHS(val : FP16) -> FP32 = (0xffff , val) as FP32 + function NaNBoxSD(val : FP32) -> FP64 = (0xffff'ffff , val) as FP64 + function NaNBoxDQ(val : FP64) -> FP128 = (0xffff'ffff'ffff'ffff, val) as FP128 + + model NaNBoxQ (val : Ex) : Ex = { $val } + model NaNBoxD (val : Ex) : Ex = { match : Ex ($FSize = FSize64 => $val; _ => $NaNBoxQ(NaNBoxDQ($val))) } + model NaNBoxS (val : Ex) : Ex = { match : Ex ($FSize = FSize32 => $val; _ => $NaNBoxD(NaNBoxSD($val))) } + model NaNBoxH (val : Ex) : Ex = { $NaNBoxS(NaNBoxHS($val)) } + + model NaNBox (size : FSizeRec, val : Ex) : Ex = { + match : Ex ( + $size.size = 16 => $NaNBoxH($val); + $size.size = 32 => $NaNBoxS($val); + $size.size = 64 => $NaNBoxD($val); + _ => $NaNBoxQ($val) + ) + } + + model-type BoolModel = (Ex, Ex) -> Ex + model Unsigned (t : Ex, f : Ex) : Ex = { t } + model Signed (t : Ex, f : Ex) : Ex = { t } + + model FRtypeInstrBiArith (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr2rm (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; $funct5 ; + let result, flags = $fun(F(rs1) as $size.ty, F(rs2) as $size.ty, FCSR.fflags /* , $rm */) in { + F(rd) := $NaNBox($size ; result) + FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) + } + )) + } + + model FRtypeInstrMinMax (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr2 (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; $funct5 ; + let result, flags = $fun(F(rs1) as $size.ty, F(rs2) as $size.ty, FCSR.fflags) in { + F(rd) := $NaNBox($size ; result) + FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) + } + )) + } + + model FRtypeInstrSqrt (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1rm (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; $funct5 ; + let result, flags = $fun(F(rs1) as $size.ty, FCSR.fflags /* , $rm */) in { + F(rd) := $NaNBox($size ; result) + FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) + } + ) ; $rs2 ) + } + + model FRtypeInstrCmp (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr2 (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; $funct5 ; + let result, flags = $fun(F(rs1) as $size.ty, F(rs2) as $size.ty, (FCSR.nv, 0b1111)) in { + X(rd) := result as UIntR // zero extend + FCSR.nv := flags.nv + } + )) + } + + model FRtypeInstrSgn (name : Id, size : FSizeRec, sgn : Ex, funct5 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr2 (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; $funct5 ; + let lower = F(rs1)(($size.size-2)..0) in + let rs1Sgn = F(rs1)($size.size-1) in + let rs2Sgn = F(rs2)($size.size-1) in + F(rd) := $NaNBox($size ; ($sgn, lower) as $size.ty) + )) + } + + model FRtypeInstrMvF2X (name : Id, size : FSizeRec, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1 (( + AsId($name, "X", $size.iSuffix) ; AsStr($name, ".X.", $size.iSuffix) ; + $rm ; $size.fmt ; $funct5 ; + // first cast to the correct float type and then sign extend + X(rd) := F(rs1) as $size.ty as SIntR + ) ; $rs2 ) + } + + model FRtypeInstrMvX2F (name : Id, size : FSizeRec, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1 (( + AsId($name, $size.iSuffix, "X") ; AsStr($name, ".", $size.iSuffix, ".X") ; + $rm ; $size.fmt ; $funct5 ; + F(rd) := $NaNBox($size ; X(rs1) as $size.ty) + ) ; $rs2 ) + } + + model FRtypeInstrCvtX2F (name : Id, fSize : FSizeRec, iSize : FSizeRec, u : BoolModel, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1rm (( + AsId($name, $fSize.suffix, $iSize.iSuffix, $u("U" ; "")) ; + AsStr($name, ".", $fSize.suffix, ".", $iSize.iSuffix, $u("U" ; "")) ; + $rm ; $fSize.fmt ; $funct5 ; + let result, flags = $fun(X(rs1) as $u(iSize.uTy ; iSize.sTy), FCSR.fflags /* , $rm */) in { + F(rd) := $NaNBox($fSize ; result) + FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) + } + ) ; $rs2 ) + } + + model FRtypeInstrCvtF2X (name : Id, fSize : FSizeRec, iSize : FSizeRec, u : BoolModel, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1rm (( + AsId($name, $iSize.iSuffix, $u("U" ; ""), $fSize.suffix) ; + AsStr($name, ".", $iSize.iSuffix, $u("U" ; ""), ".", $fSize.suffix) ; + $rm ; $fSize.fmt ; $funct5 ; + // TODO: we must make sure the builtin understands what type to convert to + let result, flags = $fun(F(rs1) as $fSize.ty, FCSR.fflags /* , $rm */) in { + // will this affect result type of builtin? It should for it to work! + X(rd) := result as $u(iSize.uTy ; iSize.sTy) + FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) + } + ) ; $rs2 ) + } + + model FRtypeInstrClass (name : Id, suffix : Id, ty : Id) : IsaDefs = { + instruction AsId($name, $suffix) : Rtype = + let f = F(rs1) as ty in + // TODO: this is from QEMU's risc-v vector_helper.c fclass_s(...) + // what predicates should we implement? + let neg = VADL::fisneg(f) in + X(rd) := if VADL::fisinf (f) then ( if neg then 1 << 0 else 1 << 7 ) else + if VADL::fiszero (f) then ( if neg then 1 << 3 else 1 << 4 ) else + if VADL::fisdenorm(f) then ( if neg then 1 << 2 else 1 << 5 ) else + if VADL::fissnan (f) then 1 << 8 else + if VADL::fisqnan (f) then 1 << 9 else + ( if neg then 1 << 1 else 1 << 6 ) + encoding AsId($name, $suffix) = {opcode = 0b101'0011, funct3 = 0b001, rs2 = 0b0'0000, funct7 = 0b111'0000} + assembly AsId($name, $suffix) = (AsStr($name), ".", AsStr($suffix), " ", register(rd), ",", register(rs1)) + } + + model FLtypeInstr (name : Id, size : FSizeRec, funct3 : Bin) : IsaDefs = { + instruction $name : Itype = + let addr = X(rs1) + immS in + let bytes = $size.size / 8 in + F(rd) := $NaNBox($size ; MEM(addr) as $size.ty) + encoding $name = {opcode = 0b000'0111, funct3 = $funct3} + assembly $name = (mnemonic, " ", register(rd), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") + } + + model FStypeInstr (name : Id, size : FSizeRec, funct3 : Bin) : IsaDefs = { + instruction $name : Stype = + let addr = X(rs1) + immS in + let bytes = $size.size / 8 in + MEM(addr) := F(rs2) as $size.ty + encoding $name = {opcode = 0b010'0111, funct3 = $funct3} + assembly $name = (mnemonic, " ", register(rs2), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") + } + + model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + instruction AsId($name, $size.suffix) : R4type = + let result, flags = $fun(F(rs1), F(rs2), F(rs3), FCSR.fflags /* , $rm */) in { + F(rd) := $NaNBox($size ; result) + FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) + } + encoding AsId($name, $size.suffix) = {opcode = 0b100'0011, funct3 = $rm, fmt = $size.fmt} + assembly AsId($name, $size.suffix) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($rm)) + } + + $FLtypeInstr (FLW ; $FSize32 ; 0b010) + $FStypeInstr (FSW ; $FSize32 ; 0b010) + + $FRtypeInstrBiArith (FADD ; $FSize32 ; VADL::fadds ; 0b0'0000 ; Frm::rne) + //$FRtypeInstrBiArith (FSUB ; $FSize32 ; VADL::fsubs ; 0b0'0001 ; Frm::rne) + //$FRtypeInstrBiArith (FMUL ; $FSize32 ; VADL::fmuls ; 0b0'0010 ; Frm::rne) + //$FRtypeInstrBiArith (FDIV ; $FSize32 ; VADL::fdivs ; 0b0'0011 ; Frm::rne) + + //$FRtypeInstrMinMax (FMIN ; $FSize32 ; VADL::fmins ; 0b0'0101 ; 0b000) + //$FRtypeInstrMinMax (FMAX ; $FSize32 ; VADL::fmaxs ; 0b0'0101 ; 0b001) + + //$FRtypeInstrSqrt (FSQRT ; $FSize32 ; VADL::fsqrts ; 0b0'1011 ; 0b0'0000 ; Frm::rne) + + //// TODO: specify somehow that lt, le are signaling and eq is a quiet comparison + //$FRtypeInstrCmp (FLE ; $FSize32 ; VADL::fles ; 0b1'0100 ; 0b000) + //$FRtypeInstrCmp (FLT ; $FSize32 ; VADL::flts ; 0b1'0100 ; 0b001) + //$FRtypeInstrCmp (FEQ ; $FSize32 ; VADL::feqs ; 0b1'0100 ; 0b010) + + $FRtypeInstrSgn (FSGNJ ; $FSize32 ; ( rs2Sgn) ; 0b0'0100 ; 0b000) + $FRtypeInstrSgn (FSGNJN ; $FSize32 ; ( 1 - rs2Sgn) ; 0b0'0100 ; 0b001) + $FRtypeInstrSgn (FSGNJX ; $FSize32 ; (rs1Sgn ^ rs2Sgn) ; 0b0'0100 ; 0b010) + + $FRtypeInstrMvF2X (FMV ; $FSize32 ; 0b1'1100 ; 0b000 ; 0b0'0000) + $FRtypeInstrMvX2F (FMV ; $FSize32 ; 0b1'1110 ; 0b000 ; 0b0'0000) + + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvts ; 0b1'1010 ; 0b0'0000 ; Frm::rne) + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvts ; 0b1'1010 ; 0b0'0001 ; Frm::rne) + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvts ; 0b1'1010 ; 0b0'0010 ; Frm::rne) + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvts ; 0b1'1010 ; 0b0'0011 ; Frm::rne) + + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvts ; 0b1'1000 ; 0b0'0000 ; Frm::rne) + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvts ; 0b1'1000 ; 0b0'0001 ; Frm::rne) + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvts ; 0b1'1000 ; 0b0'0010 ; Frm::rne) + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvts ; 0b1'1000 ; 0b0'0011 ; Frm::rne) + + //$FR4typeInstr (FMADD ; $FSize32 ; VADL::fmadds ; Frm::rne) + //$FR4typeInstr (FMSUB ; $FSize32 ; VADL::fmsubs ; Frm::rne) + //$FR4typeInstr (FNMADD ; $FSize32 ; VADL::fnmadds ; Frm::rne) + //$FR4typeInstr (FNMSUB ; $FSize32 ; VADL::fnmsubs ; Frm::rne) + + //$FRtypeInstrClass (FCLASS ; S ; FP32) + +} + +[ htif ] +processor Spike implements RV64IMF = { + constant reset_vec_addr = 0x1000 + + reset = { + PC := reset_vec_addr + } + + [ firmware ] + [ base: 0x80000000 ] + memory region [RAM] DRAM in MEM + + memory region [ROM] MROM in MEM = { + MEM<4>(0x1000) := 0x00000297 // auipc t0, 0x0 + MEM<4>(0x1004) := 0x02828613 // addi a2, t0, 40 + // TODO: this is not quite right: + // this processor has no zicsr extension + MEM<4>(0x1008) := 0x00000013 // addi x0, x0, 0 + MEM<4>(0x100c) := 0x0202b583 // ld a1, 32(t0) + MEM<4>(0x1010) := 0x0182b283 // ld t0, 24(t0) + MEM<4>(0x1014) := 0x00028067 // jr t0 + // store start_addr in memory (0x80000000) + MEM<4>(0x1018) := 0x80000000 // lo32(start_addr) + MEM<4>(0x101c) := 0x00000000 // hi32(start_addr) + // we do not yet support a fdt, but we set the address, + // to keep the registers consistent with upstream + MEM<4>(0x1020) := 0x87e00000 // lo32(fdt_addr) + MEM<4>(0x1024) := 0x00000000 // hi32(fdt_addr) + } +} diff --git a/vadl-frontend/main/vadl/ast/TypeChecker.java b/vadl-frontend/main/vadl/ast/TypeChecker.java index f744482ce..d0c163577 100644 --- a/vadl-frontend/main/vadl/ast/TypeChecker.java +++ b/vadl-frontend/main/vadl/ast/TypeChecker.java @@ -175,6 +175,8 @@ import vadl.types.ConcreteRelationType; import vadl.types.DataType; import vadl.types.FetchResultType; +import vadl.types.FloatStatusType; +import vadl.types.FloatType; import vadl.types.GroupType; import vadl.types.InstructionType; import vadl.types.MicroArchitectureType; @@ -753,6 +755,13 @@ private static boolean canImplicitCast(Type from, Type to) { } } + // FPn => Bits + if (from.getClass() == FloatType.class) { + if (to.getClass() == BitsType.class) { + return ((FloatType) from).bitWidth() == ((BitsType) to).bitWidth(); + } + } + // Bits<1> => Bool if (from.getClass() == BitsType.class) { if (to.getClass() == BoolType.class) { @@ -3730,6 +3739,8 @@ private ParsedTypeLiteralResult internalParseTypeLiteral(TypeLiteral expr, Map> unSizedBuiltins = Map.of( "Bool", Type::bool, "String", Type::string, + "FP32", Type::float32, + "FP64", Type::float64, "Instruction", MicroArchitectureType::instruction, "FetchResult", MicroArchitectureType::fetchResult ); @@ -4198,6 +4209,17 @@ private void visitSubCall(CallIndexExpr expr, Type typeBeforeSubCall) { var fieldType = Type.bool(); visitSliceIndexCall(expr, fieldType, subCall.argsIndices); type = expr.type; + } else if (type instanceof FloatStatusType) { + var allowedStatusfields = List.of("nv", "dz", "of", "uf", "nx"); + if (!allowedStatusfields.contains(fieldName)) { + var suggestions = Levenshtein.sortAll(fieldName, allowedStatusfields); + addErrorAndStopChecking(error("Unknown float status field `%s`".formatted(fieldName), expr) + .suggestions(suggestions) + .build()); + } + var fieldType = Type.bool(); + visitSliceIndexCall(expr, fieldType, subCall.argsIndices); + type = expr.type; } else if (type instanceof InstructionType) { var allowedStatusfields = List.of("address", "read", "unknown", "compute", "verify", "write", "readOrForward", diff --git a/vadl/main/vadl/types/BuiltInTable.java b/vadl/main/vadl/types/BuiltInTable.java index 34f6e41e5..f64f0e139 100644 --- a/vadl/main/vadl/types/BuiltInTable.java +++ b/vadl/main/vadl/types/BuiltInTable.java @@ -1039,6 +1039,19 @@ public class BuiltInTable { .build(); + ///// FLOAT ARITHMETIC ////// + + + public static final BuiltIn FADDS = + func("VADL::fadds", + Type.relation( + List.of(FloatType.class, FloatType.class, BitsType.class), StructType.class)) + .takesData(args -> args.get(0).bitWidth() == args.get(1).bitWidth() + && args.get(2).bitWidth() == 5) + .returnsFirstFloatAndStatus() + .build(); + + ///// FUNCTIONS ////// /** @@ -1409,6 +1422,10 @@ private static BuiltIn instr(String name) { CTO ); + public static final List FLOAT_ARITHMETIC_BUILT_INS = List.of( + FADDS + ); + public static final List FUNCTION_BUILT_INS = List.of( MNEMONIC, CONCATENATE_STRINGS, @@ -1445,6 +1462,7 @@ private static BuiltIn instr(String name) { COMPARISON_BUILT_INS.stream(), SHIFTING_BUILT_INS.stream(), BITWISE_COUNTING_BUILT_INS.stream(), + FLOAT_ARITHMETIC_BUILT_INS.stream(), FUNCTION_BUILT_INS.stream(), ASM_PARSER_BUILT_INS_LIST.stream(), MICRO_ARCHITECTURE_BUILT_INS.stream() @@ -1859,6 +1877,18 @@ public BuiltInBuilder returnsFirstBitWidthAndStatus( return this; } + public BuiltInBuilder returnsFirstFloatAndStatus() { + returnsFromFirstAsDataType((firstDataType) -> { + var valType = constructDataType(FloatType.class, firstDataType.bitWidth()); + Objects.requireNonNull(valType); + return Type.struct( + BUILTIN_RESULT, valType, + BUILTIN_STATUS, Type.floatStatus() + ); + }); + return this; + } + public BuiltInBuilder returnsFromFirstAsDataType(Function returnFunction) { returns((args) -> { diff --git a/vadl/main/vadl/types/FloatStatusType.java b/vadl/main/vadl/types/FloatStatusType.java new file mode 100644 index 000000000..324f75701 --- /dev/null +++ b/vadl/main/vadl/types/FloatStatusType.java @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText : © 2025 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.types; + +/** + * A class that represents the VADL float status type. + * + *

It is actually a struct of with five boolean fields. + * These five elements represent status flags: + *

  • nv
  • + *
  • dz
  • + *
  • of
  • + *
  • uf
  • + *
  • nx
  • + * in that order. + */ +public class FloatStatusType extends StructType { + + public static final String INVALID = "nv"; + public static final String DIVISION_BY_ZERO = "dz"; + public static final String OVERFLOW = "of"; + public static final String UNDERFLOW = "uf"; + public static final String INEXACT = "nx"; + + protected FloatStatusType() { + super(Type.struct( + INVALID, Type.bool(), + DIVISION_BY_ZERO, Type.bool(), + OVERFLOW, Type.bool(), + UNDERFLOW, Type.bool(), + INEXACT, Type.bool() + ).fields()); + } + + @Override + public String name() { + return "FloatStatus"; + } + +} diff --git a/vadl/main/vadl/types/FloatType.java b/vadl/main/vadl/types/FloatType.java new file mode 100644 index 000000000..47437891c --- /dev/null +++ b/vadl/main/vadl/types/FloatType.java @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText : © 2025 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.types; + +import javax.annotation.CheckForNull; + +/** + * An IEEE-754 32/64-bit float. + */ +public class FloatType extends BitsType { + + /** + * The size of the float. + */ + public enum Size { + FP32(32), + FP64(64); + + final int bitWidth; + + Size(int bitWidth) { + this.bitWidth = bitWidth; + } + } + + protected final Size size; + + protected FloatType(Size size) { + super(size.bitWidth); + this.size = size; + } + + @Override + public String name() { + return "FP%s".formatted(size.bitWidth); + } + + @CheckForNull + @Override + public DataType fittingCppType() { + return null; + } + + @Override + public boolean equals(Object obj) { + return this.getClass() == obj.getClass() && this.size == ((FloatType) obj).size; + } + + @Override + public int hashCode() { + return super.hashCode(); + } +} diff --git a/vadl/main/vadl/types/Type.java b/vadl/main/vadl/types/Type.java index 225bac78c..c71d0d661 100644 --- a/vadl/main/vadl/types/Type.java +++ b/vadl/main/vadl/types/Type.java @@ -120,6 +120,37 @@ public static UIntType unsignedInt(int bitWidth) { .computeIfAbsent(bitWidth, k -> new UIntType(bitWidth)); } + private static final HashMap floatTyps = new HashMap<>(); + + /** + * Retrieves the instance of FloatType with the specified size. + * + * @param size the size of the FloatType object + * @return the FloatType object with the specified size + */ + public static FloatType floatType(FloatType.Size size) { + return floatTyps + .computeIfAbsent(size, k -> new FloatType(size)); + } + + /** + * Retrieves the instance of FloatType with size 32. + * + * @return the FloatType object with size 32 + */ + public static FloatType float32() { + return floatType(FloatType.Size.FP32); + } + + /** + * Retrieves the instance of FloatType with size 64. + * + * @return the FloatType object with size 64 + */ + public static FloatType float64() { + return floatType(FloatType.Size.FP64); + } + /** * Returns a DummyType object. * @@ -188,6 +219,25 @@ public static StructType struct(String field1, Type type1, return struct(fields); } + /** + * Retrieves the struct type with the specified subtypes. + * + * @return the struct type with the specified subtypes + */ + public static StructType struct(String field1, Type type1, + String field2, Type type2, + String field3, Type type3, + String field4, Type type4, + String field5, Type type5) { + var fields = new LinkedHashMap(); + fields.put(field1, type1); + fields.put(field2, type2); + fields.put(field3, type3); + fields.put(field4, type4); + fields.put(field5, type5); + return struct(fields); + } + private static @Nullable StatusType statusType = null; /** @@ -202,6 +252,20 @@ public static StatusType status() { return statusType; } + private static @Nullable FloatStatusType floatStatusType = null; + + /** + * Retrieves the float status type instance. + * + * @return the float status type instance + */ + public static FloatStatusType floatStatus() { + if (floatStatusType == null) { + floatStatusType = new FloatStatusType(); + } + return floatStatusType; + } + private static @Nullable VoidType voidType = null; @@ -374,6 +438,14 @@ public static DataType constructDataType(Class typeClass, in return Type.signedInt(bitWidth); } else if (typeClass == UIntType.class) { return Type.unsignedInt(bitWidth); + } else if (typeClass == FloatType.class) { + if (bitWidth == FloatType.Size.FP32.bitWidth) { + return Type.float32(); + } else if (bitWidth == FloatType.Size.FP64.bitWidth) { + return Type.float64(); + } else { + return null; + } } else { return null; } @@ -403,6 +475,7 @@ public static GroupType group(Type elementType, UIntType lengthType, UIntType bi /// These are all the builtin types that exist in the language. /// Some of them cannot be initialized like them since they require a size, like `SInt<16>` /// which is why they are named bases. - public static final Set builtinTypeBases = - Set.of("Bool", "String", "Bits", "UInt", "SInt", "Instruction", "FetchResult"); + public static final Set builtinTypeBases = Set.of( + "Bool", "String", "Bits", "UInt", "SInt", "FP32", "FP64", "Instruction", "FetchResult" + ); } diff --git a/vadl/main/vadl/utils/VadlBuiltInFloatStatusOnlyDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInFloatStatusOnlyDispatcher.java new file mode 100644 index 000000000..3f3bba1da --- /dev/null +++ b/vadl/main/vadl/utils/VadlBuiltInFloatStatusOnlyDispatcher.java @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText : © 2025 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.utils; + +import vadl.types.BuiltInTable; + +/** + * A dispatcher that handles all {@code VADL::F*S} built-ins. + */ +public interface VadlBuiltInFloatStatusOnlyDispatcher { + + /** + * Calls the correct handler for the given built-in and uses the input as argument. + * + * @param input is passed to the handler method. + * @param builtIn to find the correct handler method. + * @return true if the handler was found and called, false otherwise. + */ + default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { + if (builtIn == BuiltInTable.FADDS) { + handleFADDS(input); + } else { + return false; + } + return true; + } + + void handleFADDS(T input); + + +} diff --git a/vadl/main/vadl/viam/passes/statusBuiltInInlinePass/StatusBuiltInInlinePass.java b/vadl/main/vadl/viam/passes/statusBuiltInInlinePass/StatusBuiltInInlinePass.java index ef52fcc86..a64ddead3 100644 --- a/vadl/main/vadl/viam/passes/statusBuiltInInlinePass/StatusBuiltInInlinePass.java +++ b/vadl/main/vadl/viam/passes/statusBuiltInInlinePass/StatusBuiltInInlinePass.java @@ -23,6 +23,7 @@ import vadl.pass.Pass; import vadl.pass.PassName; import vadl.pass.PassResults; +import vadl.utils.VadlBuiltInFloatStatusOnlyDispatcher; import vadl.utils.VadlBuiltInStatusOnlyDispatcher; import vadl.viam.Specification; import vadl.viam.graph.Graph; @@ -52,8 +53,10 @@ public PassName getName() { @Override public Object execute(PassResults passResults, Specification viam) throws IOException { return viam.isa().map(isa -> { - isa.ownInstructions().forEach(i -> - new StatusBuiltInInliner(i.behavior()).run()); + isa.ownInstructions().forEach(i -> { + new StatusBuiltInInliner(i.behavior()).run(); + new FloatStatusBuiltInInliner(i.behavior()).run(); + }); return null; }); } @@ -274,3 +277,34 @@ public void handleRRXS(BuiltInCall input) { throwNotImplemented(input); } } + +/** + * Inlines all float status built-ins for the given graph. + * There is a {@link Inliner} for each status built-in. + */ +class FloatStatusBuiltInInliner implements VadlBuiltInFloatStatusOnlyDispatcher { + + private final Graph graph; + + FloatStatusBuiltInInliner(Graph graph) { + this.graph = graph; + } + + void run() { + graph.getNodes(BuiltInCall.class).forEach(n -> { + dispatch(n, n.builtIn()); + }); + } + + private void throwNotImplemented(BuiltInCall input) { + throw Diagnostic.error("Built-In Lowering Not Implemented", input) + .description("OpenVADL does not yet implement the inlining of the %s built-in.", + input.builtIn().name()) + .build(); + } + + @Override + public void handleFADDS(BuiltInCall input) { + throwNotImplemented(input); + } +} \ No newline at end of file From 8c5a1b28fd0e3b0e4d1f3489c63ba6bb72feadfa Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Sun, 19 Jul 2026 11:35:04 +0200 Subject: [PATCH 02/11] wip: Add float-type definition --- sys/risc-v/rv64f.vadl | 93 ++++++++------- vadl-frontend/main/vadl/ast/AstUtils.java | 4 +- .../main/vadl/ast/BehaviorLowering.java | 20 +++- .../main/vadl/ast/MacroExpander.java | 9 ++ vadl-frontend/main/vadl/ast/ModelRemover.java | 6 + vadl-frontend/main/vadl/ast/TypeChecker.java | 57 ++++++---- vadl-frontend/main/vadl/ast/Ungrouper.java | 7 ++ vadl-frontend/main/vadl/ast/ViamLowering.java | 11 +- .../vadl/ast/nodes/DefinitionVisitor.java | 2 + .../vadl/ast/nodes/FloatTypeDefinition.java | 80 +++++++++++++ .../vadl/ast/nodes/RecursiveAstVisitor.java | 8 ++ vadl-frontend/main/vadl/ast/vadl.ATG | 7 ++ .../templates/iss/target/gen-arch/cpu.c | 3 + .../templates/iss/target/gen-arch/cpu.h | 2 + .../templates/iss/target/gen-arch/helper.c | 5 + .../templates/iss/target/gen-arch/helper.h | 6 +- .../passes/common/IssNormalizationPass.java | 5 + .../tcg/lowering/TcgOpLoweringPass.java | 20 +++- vadl/main/vadl/types/BuiltInTable.java | 53 +++++++-- vadl/main/vadl/types/FloatType.java | 45 +------- vadl/main/vadl/types/RelationType.java | 21 ++++ vadl/main/vadl/types/Type.java | 67 +++++------ .../VadlBuiltInEmptyNoStatusDispatcher.java | 4 + .../utils/VadlBuiltInNoStatusDispatcher.java | 4 + vadl/main/vadl/viam/DefinitionVisitor.java | 13 +++ vadl/main/vadl/viam/FloatFormat.java | 88 +++++++++++++++ .../graph/dependency/FloatBuiltInCall.java | 106 ++++++++++++++++++ 27 files changed, 583 insertions(+), 163 deletions(-) create mode 100644 vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java create mode 100644 vadl/main/vadl/viam/FloatFormat.java create mode 100644 vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java diff --git a/sys/risc-v/rv64f.vadl b/sys/risc-v/rv64f.vadl index 391555da8..555e29b89 100644 --- a/sys/risc-v/rv64f.vadl +++ b/sys/risc-v/rv64f.vadl @@ -11,8 +11,9 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { constant FSize64 : ConstTy = 64 constant FSize128 : ConstTy = 128 - // TODO: these need to be built-in types using FP16 = Bits<16> + using FP32 = Bits<32> + using FP64 = Bits<64> using FP128 = Bits<128> using SIntH = SInt<16> @@ -30,6 +31,11 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { register FCSR : FCsrFormat + //[ sticky float flag : invalid, nv ] + //[ sticky float flag : divide_by_zero, dz ] + //[ sticky float flag : overflow, of ] + //[ sticky float flag : underflow, uf ] + //[ sticky float flag : inexact, nx ] format FCsrFormat : Bits<32> = { reserved [31..8] , frm [7..5] // Rounding mode @@ -116,12 +122,23 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($c.rm))) } - record FSizeRec (suffix : Id, iSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, size : Int) + record FSizeRec (suffix : Id, iSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, fTy : Id, size : Int) - model FSize16 () : FSizeRec = {(H ; H ; Fmt::h ; FP16 ; SIntH ; UIntH ; 16 )} - model FSize32 () : FSizeRec = {(S ; W ; Fmt::s ; FP32 ; SIntW ; UIntW ; 32 )} - model FSize64 () : FSizeRec = {(D ; D ; Fmt::d ; FP64 ; SIntD ; UIntD ; 64 )} - model FSize128 () : FSizeRec = {(Q ; Q ; Fmt::q ; FP128 ; SIntQ ; UIntQ ; 128)} + //[ canonical sNaN : 0x7fa00000 ] + //[ canonical qNaN : 0x7fc00000 ] + //[ IEEE float : 32 ] + float-type IEEE32 + + //[ canonical sNaN : 0x7fa00000'00000000 ] + //[ canonical qNaN : 0x7fc00000'00000000 ] + //[ IEEE float : 64 ] + float-type IEEE64 + + // TODO: IEEE16 and IEEE128 are not yet implemented + model FSize16 () : FSizeRec = {(H ; H ; Fmt::h ; FP16 ; SIntH ; UIntH ; IEEE16 ; 16 )} + model FSize32 () : FSizeRec = {(S ; W ; Fmt::s ; FP32 ; SIntW ; UIntW ; IEEE32 ; 32 )} + model FSize64 () : FSizeRec = {(D ; D ; Fmt::d ; FP64 ; SIntD ; UIntD ; IEEE64 ; 64 )} + model FSize128 () : FSizeRec = {(Q ; Q ; Fmt::q ; FP128 ; SIntQ ; UIntQ ; IEEE128 ; 128)} function NaNBoxHS(val : FP16) -> FP32 = (0xffff , val) as FP32 function NaNBoxSD(val : FP32) -> FP64 = (0xffff'ffff , val) as FP64 @@ -149,9 +166,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstr2rm (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; $funct5 ; - let result, flags = $fun(F(rs1) as $size.ty, F(rs2) as $size.ty, FCSR.fflags /* , $rm */) in { + let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty, $rm) in { F(rd) := $NaNBox($size ; result) - FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } )) } @@ -160,9 +176,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; $funct5 ; - let result, flags = $fun(F(rs1) as $size.ty, F(rs2) as $size.ty, FCSR.fflags) in { + let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { F(rd) := $NaNBox($size ; result) - FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } )) } @@ -171,9 +186,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstr1rm (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; $funct5 ; - let result, flags = $fun(F(rs1) as $size.ty, FCSR.fflags /* , $rm */) in { + let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { F(rd) := $NaNBox($size ; result) - FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } ) ; $rs2 ) } @@ -182,9 +196,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; $funct5 ; - let result, flags = $fun(F(rs1) as $size.ty, F(rs2) as $size.ty, (FCSR.nv, 0b1111)) in { + let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { X(rd) := result as UIntR // zero extend - FCSR.nv := flags.nv } )) } @@ -222,9 +235,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { AsId($name, $fSize.suffix, $iSize.iSuffix, $u("U" ; "")) ; AsStr($name, ".", $fSize.suffix, ".", $iSize.iSuffix, $u("U" ; "")) ; $rm ; $fSize.fmt ; $funct5 ; - let result, flags = $fun(X(rs1) as $u(iSize.uTy ; iSize.sTy), FCSR.fflags /* , $rm */) in { + let result = $fun($fSize.fTy, X(rs1) as $u(iSize.uTy ; iSize.sTy), $rm) in { F(rd) := $NaNBox($fSize ; result) - FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } ) ; $rs2 ) } @@ -235,8 +247,7 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { AsStr($name, ".", $iSize.iSuffix, $u("U" ; ""), ".", $fSize.suffix) ; $rm ; $fSize.fmt ; $funct5 ; // TODO: we must make sure the builtin understands what type to convert to - let result, flags = $fun(F(rs1) as $fSize.ty, FCSR.fflags /* , $rm */) in { - // will this affect result type of builtin? It should for it to work! + let result, flags = $fun($fSize.fTy, F(rs1) as $fSize.ty, $rm) in { X(rd) := result as $u(iSize.uTy ; iSize.sTy) FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } @@ -279,7 +290,7 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { instruction AsId($name, $size.suffix) : R4type = - let result, flags = $fun(F(rs1), F(rs2), F(rs3), FCSR.fflags /* , $rm */) in { + let result, flags = $fun($size.fTy, F(rs1), F(rs2), F(rs3), $rm) in { F(rd) := $NaNBox($size ; result) FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } @@ -290,20 +301,20 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FLtypeInstr (FLW ; $FSize32 ; 0b010) $FStypeInstr (FSW ; $FSize32 ; 0b010) - $FRtypeInstrBiArith (FADD ; $FSize32 ; VADL::fadds ; 0b0'0000 ; Frm::rne) - //$FRtypeInstrBiArith (FSUB ; $FSize32 ; VADL::fsubs ; 0b0'0001 ; Frm::rne) - //$FRtypeInstrBiArith (FMUL ; $FSize32 ; VADL::fmuls ; 0b0'0010 ; Frm::rne) - //$FRtypeInstrBiArith (FDIV ; $FSize32 ; VADL::fdivs ; 0b0'0011 ; Frm::rne) + $FRtypeInstrBiArith (FADD ; $FSize32 ; VADL::fadd ; 0b0'0000 ; Frm::rne) + //$FRtypeInstrBiArith (FSUB ; $FSize32 ; VADL::fsub ; 0b0'0001 ; Frm::rne) + //$FRtypeInstrBiArith (FMUL ; $FSize32 ; VADL::fmul ; 0b0'0010 ; Frm::rne) + //$FRtypeInstrBiArith (FDIV ; $FSize32 ; VADL::fdiv ; 0b0'0011 ; Frm::rne) - //$FRtypeInstrMinMax (FMIN ; $FSize32 ; VADL::fmins ; 0b0'0101 ; 0b000) - //$FRtypeInstrMinMax (FMAX ; $FSize32 ; VADL::fmaxs ; 0b0'0101 ; 0b001) + //$FRtypeInstrMinMax (FMIN ; $FSize32 ; VADL::fmin ; 0b0'0101 ; 0b000) + //$FRtypeInstrMinMax (FMAX ; $FSize32 ; VADL::fmax ; 0b0'0101 ; 0b001) - //$FRtypeInstrSqrt (FSQRT ; $FSize32 ; VADL::fsqrts ; 0b0'1011 ; 0b0'0000 ; Frm::rne) + //$FRtypeInstrSqrt (FSQRT ; $FSize32 ; VADL::fsqrt ; 0b0'1011 ; 0b0'0000 ; Frm::rne) //// TODO: specify somehow that lt, le are signaling and eq is a quiet comparison - //$FRtypeInstrCmp (FLE ; $FSize32 ; VADL::fles ; 0b1'0100 ; 0b000) - //$FRtypeInstrCmp (FLT ; $FSize32 ; VADL::flts ; 0b1'0100 ; 0b001) - //$FRtypeInstrCmp (FEQ ; $FSize32 ; VADL::feqs ; 0b1'0100 ; 0b010) + //$FRtypeInstrCmp (FLE ; $FSize32 ; VADL::fle ; 0b1'0100 ; 0b000) + //$FRtypeInstrCmp (FLT ; $FSize32 ; VADL::flt ; 0b1'0100 ; 0b001) + //$FRtypeInstrCmp (FEQ ; $FSize32 ; VADL::feq ; 0b1'0100 ; 0b010) $FRtypeInstrSgn (FSGNJ ; $FSize32 ; ( rs2Sgn) ; 0b0'0100 ; 0b000) $FRtypeInstrSgn (FSGNJN ; $FSize32 ; ( 1 - rs2Sgn) ; 0b0'0100 ; 0b001) @@ -312,20 +323,20 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstrMvF2X (FMV ; $FSize32 ; 0b1'1100 ; 0b000 ; 0b0'0000) $FRtypeInstrMvX2F (FMV ; $FSize32 ; 0b1'1110 ; 0b000 ; 0b0'0000) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvts ; 0b1'1010 ; 0b0'0000 ; Frm::rne) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvts ; 0b1'1010 ; 0b0'0001 ; Frm::rne) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvts ; 0b1'1010 ; 0b0'0010 ; Frm::rne) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvts ; 0b1'1010 ; 0b0'0011 ; Frm::rne) + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvt ; 0b1'1010 ; 0b0'0000 ; Frm::rne) + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvt ; 0b1'1010 ; 0b0'0001 ; Frm::rne) + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvt ; 0b1'1010 ; 0b0'0010 ; Frm::rne) + //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvt ; 0b1'1010 ; 0b0'0011 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvts ; 0b1'1000 ; 0b0'0000 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvts ; 0b1'1000 ; 0b0'0001 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvts ; 0b1'1000 ; 0b0'0010 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvts ; 0b1'1000 ; 0b0'0011 ; Frm::rne) + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvt ; 0b1'1000 ; 0b0'0000 ; Frm::rne) + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvt ; 0b1'1000 ; 0b0'0001 ; Frm::rne) + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvt ; 0b1'1000 ; 0b0'0010 ; Frm::rne) + //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvt ; 0b1'1000 ; 0b0'0011 ; Frm::rne) - //$FR4typeInstr (FMADD ; $FSize32 ; VADL::fmadds ; Frm::rne) - //$FR4typeInstr (FMSUB ; $FSize32 ; VADL::fmsubs ; Frm::rne) - //$FR4typeInstr (FNMADD ; $FSize32 ; VADL::fnmadds ; Frm::rne) - //$FR4typeInstr (FNMSUB ; $FSize32 ; VADL::fnmsubs ; Frm::rne) + //$FR4typeInstr (FMADD ; $FSize32 ; VADL::fmadd ; Frm::rne) + //$FR4typeInstr (FMSUB ; $FSize32 ; VADL::fmsub ; Frm::rne) + //$FR4typeInstr (FNMADD ; $FSize32 ; VADL::fnmadd ; Frm::rne) + //$FR4typeInstr (FNMSUB ; $FSize32 ; VADL::fnmsub ; Frm::rne) //$FRtypeInstrClass (FCLASS ; S ; FP32) diff --git a/vadl-frontend/main/vadl/ast/AstUtils.java b/vadl-frontend/main/vadl/ast/AstUtils.java index c6ca9ae89..6ce7d3b8c 100644 --- a/vadl-frontend/main/vadl/ast/AstUtils.java +++ b/vadl-frontend/main/vadl/ast/AstUtils.java @@ -73,9 +73,7 @@ static BuiltInTable.BuiltIn getBuiltIn(String name, List argTypes) { name = "sdec"; } - String finalBuiltinName = name; - var matchingBuiltin = nameLookupTable.get(finalBuiltinName); - return matchingBuiltin; + return nameLookupTable.get(name); } static BuiltInTable.BuiltIn getOperatorBuiltIn(Operator operator, List argTypes) { diff --git a/vadl-frontend/main/vadl/ast/BehaviorLowering.java b/vadl-frontend/main/vadl/ast/BehaviorLowering.java index b138894d5..78f0592d4 100644 --- a/vadl-frontend/main/vadl/ast/BehaviorLowering.java +++ b/vadl-frontend/main/vadl/ast/BehaviorLowering.java @@ -63,6 +63,7 @@ import vadl.ast.nodes.ExpandedSequenceCallExpr; import vadl.ast.nodes.Expr; import vadl.ast.nodes.ExprVisitor; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.ForallExpr; import vadl.ast.nodes.ForallStatement; import vadl.ast.nodes.ForallThenExpr; @@ -132,6 +133,7 @@ import vadl.viam.Counter; import vadl.viam.Definition; import vadl.viam.ExceptionDef; +import vadl.viam.FloatFormat; import vadl.viam.Format; import vadl.viam.Function; import vadl.viam.Instruction; @@ -165,6 +167,7 @@ import vadl.viam.graph.dependency.ExpressionNode; import vadl.viam.graph.dependency.FieldAccessRefNode; import vadl.viam.graph.dependency.FieldRefNode; +import vadl.viam.graph.dependency.FloatBuiltInCall; import vadl.viam.graph.dependency.FoldNode; import vadl.viam.graph.dependency.ForIdxNode; import vadl.viam.graph.dependency.FuncCallNode; @@ -1387,7 +1390,19 @@ public ExpressionNode visit(CallIndexExpr expr) { var argGroups = expr.args(); final var args = new NodeList(AstUtils.argumentCount(argGroups)); - AstUtils.forEachArgument(argGroups, arg -> args.add(this.fetch(arg))); + final var floatTypeArgs = new ArrayList(); + AstUtils.forEachArgument(argGroups, arg -> { + var target = switch (arg) { + case Identifier identifier -> identifier.target(); + case IdentifierPath path -> path.target(); + default -> null; + }; + if (target instanceof FloatTypeDefinition floatTypeDef) { + floatTypeArgs.add((FloatFormat) viamLowering.fetch(floatTypeDef).orElseThrow()); + } else { + args.add(this.fetch(arg)); + } + }); var typeBeforeSlice = getViamType(expr.typeBeforeSlice()); ExpressionNode exprBeforeSlice; @@ -1397,6 +1412,9 @@ public ExpressionNode visit(CallIndexExpr expr) { if (BuiltInTable.ASM_PARSER_BUILT_INS.contains(expr.computedBuiltIn)) { exprBeforeSlice = new AsmBuiltInCall(expr.computedBuiltIn, args, typeBeforeSlice); + } else if (BuiltInTable.FLOAT_BUILT_INS.contains(expr.computedBuiltIn)) { + exprBeforeSlice = new FloatBuiltInCall(expr.computedBuiltIn, args, + floatTypeArgs, typeBeforeSlice); } else { exprBeforeSlice = new BuiltInCall(expr.computedBuiltIn, args, typeBeforeSlice); diff --git a/vadl-frontend/main/vadl/ast/MacroExpander.java b/vadl-frontend/main/vadl/ast/MacroExpander.java index d387ae9e1..c1cd3a7eb 100644 --- a/vadl-frontend/main/vadl/ast/MacroExpander.java +++ b/vadl-frontend/main/vadl/ast/MacroExpander.java @@ -73,6 +73,7 @@ import vadl.ast.nodes.ExpandedSequenceCallExpr; import vadl.ast.nodes.Expr; import vadl.ast.nodes.ExprVisitor; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.ForallExpr; import vadl.ast.nodes.ForallIndex; import vadl.ast.nodes.ForallStatement; @@ -707,6 +708,14 @@ public Definition visit(ConstantDefinition definition) { ); } + @Override + public Definition visit(FloatTypeDefinition definition) { + return new FloatTypeDefinition( + expandExpr(definition.identifier), + copyLoc(definition.loc) + ); + } + @Override public Definition visit(FormatDefinition definition) { var fields = new ArrayList(definition.fields.size()); diff --git a/vadl-frontend/main/vadl/ast/ModelRemover.java b/vadl-frontend/main/vadl/ast/ModelRemover.java index 40b6525f7..c1f7b4c86 100644 --- a/vadl-frontend/main/vadl/ast/ModelRemover.java +++ b/vadl-frontend/main/vadl/ast/ModelRemover.java @@ -47,6 +47,7 @@ import vadl.ast.nodes.EncodingFormatField; import vadl.ast.nodes.EnumerationDefinition; import vadl.ast.nodes.ExceptionDefinition; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.FormatDefinition; import vadl.ast.nodes.FunctionDefinition; import vadl.ast.nodes.GroupDefinition; @@ -112,6 +113,11 @@ public Definition visit(ConstantDefinition definition) { return definition; } + @Override + public Definition visit(FloatTypeDefinition definition) { + return definition; + } + @Override public Definition visit(FormatDefinition definition) { return definition; diff --git a/vadl-frontend/main/vadl/ast/TypeChecker.java b/vadl-frontend/main/vadl/ast/TypeChecker.java index d0c163577..8effef5a4 100644 --- a/vadl-frontend/main/vadl/ast/TypeChecker.java +++ b/vadl-frontend/main/vadl/ast/TypeChecker.java @@ -90,6 +90,7 @@ import vadl.ast.nodes.ExpandedSequenceCallExpr; import vadl.ast.nodes.Expr; import vadl.ast.nodes.ExprVisitor; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.ForallExpr; import vadl.ast.nodes.ForallStatement; import vadl.ast.nodes.ForallThenExpr; @@ -755,13 +756,6 @@ private static boolean canImplicitCast(Type from, Type to) { } } - // FPn => Bits - if (from.getClass() == FloatType.class) { - if (to.getClass() == BitsType.class) { - return ((FloatType) from).bitWidth() == ((BitsType) to).bitWidth(); - } - } - // Bits<1> => Bool if (from.getClass() == BitsType.class) { if (to.getClass() == BoolType.class) { @@ -995,8 +989,9 @@ private BuiltInCheckResult checkBuiltin(BuiltInTable.BuiltIn builtIn, List private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, List args, WithLocation location) { - if (!(args.size() == builtIn.argTypeClasses().size() || (builtIn.signature().hasVarArgs() - && args.size() >= builtIn.argTypeClasses().size()))) { + int minArgCount = builtIn.argTypeClasses().size() + builtIn.signature().floatTypeArgCount(); + if (!(args.size() == minArgCount + || (builtIn.signature().hasVarArgs() && args.size() >= minArgCount))) { throw addErrorAndStopChecking( error("Type Mismatch", location) .locationDescription(location, @@ -1238,6 +1233,9 @@ private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, Li // Now revert to the generic handling of functions. } + var ftArgCnt = builtIn.signature().floatTypeArgCount(); + args = args.stream().skip(ftArgCnt).toList(); + var argTypes = args.stream().map(Expr::type).toList(); var areAllConst = argTypes.stream().allMatch(ConstantType.class::isInstance); if (areAllConst) { @@ -1263,23 +1261,34 @@ private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, Li var originalArgTypes = argTypes; argTypes = args.stream().map(Expr::type).toList(); - - if (!builtIn.takes(argTypes)) { + var ftArgs = args.stream().limit(ftArgCnt).toList(); + var ftTypes = ftArgs.stream().map(Expr::type).toList(); + var ftTypesInvalid = !ftTypes.stream().allMatch(FloatType.class::isInstance); + if (ftTypesInvalid || !builtIn.takes(argTypes)) { // FIXME: Further improve these error messages. var areSomeConst = originalArgTypes.stream().anyMatch(ConstantType.class::isInstance); - var calledTypes = String.join(", ", argTypes.stream().map(Type::toString).toList()); + var calledTypes = Stream.concat(ftTypes.stream(), argTypes.stream()) + .map(Type::toString).collect(Collectors.joining(", ")); addErrorAndStopChecking( error("Type Mismatch", location) .locationDescription(location, "The builtin has the signature `%s` but got `%s`.", - builtIn.signature(), calledTypes) + builtIn.signature().nameWithFloatTypes(), calledTypes) .applyIf(areSomeConst, b -> b.locationHelp(location, "Try casting some of the constant arguments to explicit types.")) + .applyIf(ftTypesInvalid, b -> + b.help("The first %d arguments must be float-type.", ftArgCnt)) .build()); } return new BuiltInCheckResult(argTypes, builtIn.returns(argTypes)); } + @Override + public Void visit(FloatTypeDefinition definition) { + // Nothing to do + return null; + } + @Override public Void visit(ConstantDefinition definition) { Type valType = withBranchingStrategy( @@ -3225,6 +3234,12 @@ private void visitIdentifiable(Expr expr) { return; } + if (origin instanceof FloatTypeDefinition floatTypeDef) { + check(floatTypeDef); + expr.type = floatTypeDef.type(); + return; + } + if (origin instanceof RangeFormatField field) { // FIXME: Unfortonatley the format fields need to be specified in declare-after-use for now expr.type = field.type; @@ -3739,8 +3754,6 @@ private ParsedTypeLiteralResult internalParseTypeLiteral(TypeLiteral expr, Map> unSizedBuiltins = Map.of( "Bool", Type::bool, "String", Type::string, - "FP32", Type::float32, - "FP64", Type::float64, "Instruction", MicroArchitectureType::instruction, "FetchResult", MicroArchitectureType::fetchResult ); @@ -4213,9 +4226,10 @@ private void visitSubCall(CallIndexExpr expr, Type typeBeforeSubCall) { var allowedStatusfields = List.of("nv", "dz", "of", "uf", "nx"); if (!allowedStatusfields.contains(fieldName)) { var suggestions = Levenshtein.sortAll(fieldName, allowedStatusfields); - addErrorAndStopChecking(error("Unknown float status field `%s`".formatted(fieldName), expr) - .suggestions(suggestions) - .build()); + addErrorAndStopChecking( + error("Unknown float status field `%s`".formatted(fieldName), expr) + .suggestions(suggestions) + .build()); } var fieldType = Type.bool(); visitSliceIndexCall(expr, fieldType, subCall.argsIndices); @@ -4341,7 +4355,12 @@ private void processCallOfBuiltIn(CallIndexExpr expr) { var checkResult = checkBuiltin(builtin, args, expr); if (checkResult.castedArgTypes != null) { - expr.replaceArgsFor(0, checkResult.applyCastToArgs(args)); + var ftArgCnt = builtin.signature().floatTypeArgCount(); + var newArgs = Stream.concat( + args.stream().limit(ftArgCnt), + checkResult.applyCastToArgs(args.stream().skip(ftArgCnt).toList()).stream() + ).toList(); + expr.replaceArgsFor(0, newArgs); } expr.typeBeforeSlice = checkResult.returnType; expr.argsIndices.get(0).type = checkResult.returnType; diff --git a/vadl-frontend/main/vadl/ast/Ungrouper.java b/vadl-frontend/main/vadl/ast/Ungrouper.java index 21927451b..1e8623655 100644 --- a/vadl-frontend/main/vadl/ast/Ungrouper.java +++ b/vadl-frontend/main/vadl/ast/Ungrouper.java @@ -63,6 +63,7 @@ import vadl.ast.nodes.ExpandedSequenceCallExpr; import vadl.ast.nodes.Expr; import vadl.ast.nodes.ExprVisitor; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.ForallExpr; import vadl.ast.nodes.ForallStatement; import vadl.ast.nodes.ForallThenExpr; @@ -360,6 +361,12 @@ public Expr visit(ResourceReferenceExression expr) { return expr; } + @Override + public Void visit(FloatTypeDefinition definition) { + ungroupAnnotations(definition); + return null; + } + @Override public Void visit(ConstantDefinition definition) { ungroupAnnotations(definition); diff --git a/vadl-frontend/main/vadl/ast/ViamLowering.java b/vadl-frontend/main/vadl/ast/ViamLowering.java index 88e5efe88..4a9a0b56e 100644 --- a/vadl-frontend/main/vadl/ast/ViamLowering.java +++ b/vadl-frontend/main/vadl/ast/ViamLowering.java @@ -78,6 +78,7 @@ import vadl.ast.nodes.ExpandedAliasDefSequenceCallExpr; import vadl.ast.nodes.ExpandedSequenceCallExpr; import vadl.ast.nodes.Expr; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.FormatDefinition; import vadl.ast.nodes.FormatField; import vadl.ast.nodes.FunctionDefinition; @@ -141,6 +142,7 @@ import vadl.viam.Counter; import vadl.viam.Encoding; import vadl.viam.ExceptionDef; +import vadl.viam.FloatFormat; import vadl.viam.Format; import vadl.viam.Function; import vadl.viam.Group; @@ -1095,6 +1097,13 @@ public Optional visit(CacheDefinition definition) { definition.getClass().getSimpleName())); } + @Override + public Optional visit(FloatTypeDefinition definition) { + var identifier = + new vadl.viam.Identifier(definition.viamId, definition.identifier().location()); + return Optional.of(new FloatFormat(identifier)); + } + @Override public Optional visit(ConstantDefinition definition) { // Do nothing on purpose. @@ -1391,7 +1400,7 @@ private void setFieldAccessPredicate(PredicateFormatField predField) { } /** - * Get the field encoding for the {@link vadl.ast.EncodingFormatField} + * Get the field encoding for the {@link EncodingFormatField} * with the kind {@code ENCODING}. */ @SuppressWarnings("LineLength") diff --git a/vadl-frontend/main/vadl/ast/nodes/DefinitionVisitor.java b/vadl-frontend/main/vadl/ast/nodes/DefinitionVisitor.java index 042da34f8..fc38b13f4 100644 --- a/vadl-frontend/main/vadl/ast/nodes/DefinitionVisitor.java +++ b/vadl-frontend/main/vadl/ast/nodes/DefinitionVisitor.java @@ -68,6 +68,8 @@ public interface DefinitionVisitor { public R visit(ExceptionDefinition definition); + public R visit(FloatTypeDefinition definition); + public R visit(FormatDefinition definition); public R visit(DerivedFormatField definition); diff --git a/vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java b/vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java new file mode 100644 index 000000000..c433d355e --- /dev/null +++ b/vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.ast.nodes; + +import java.util.Objects; +import vadl.types.Type; +import vadl.utils.SourceLocation; + +@SuppressWarnings({"MissingJavadocType", "MissingJavadocMethod"}) +public class FloatTypeDefinition extends Definition implements IdentifiableNode, TypedNode { + public IdentifierOrPlaceholder identifier; + + public SourceLocation loc; + + public FloatTypeDefinition(IdentifierOrPlaceholder identifier, SourceLocation loc) { + this.identifier = identifier; + this.loc = loc; + } + + @Override + public Identifier identifier() { + return (Identifier) identifier; + } + + @Override + public SourceLocation location() { + return loc; + } + + @Override + public SyntaxType syntaxType() { + return BasicSyntaxType.COMMON_DEFS; + } + + @Override + public void prettyPrint(int indent, StringBuilder builder) { + prettyPrintAnnotations(indent, builder); + builder.append(prettyIndentString(indent)); + builder.append("float-type %s".formatted(identifier().name)); + builder.append("\n"); + } + + @Override + public R accept(DefinitionVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + FloatTypeDefinition that = (FloatTypeDefinition) o; + return Objects.equals(identifier, that.identifier); + } + + @Override + public int hashCode() { + return Objects.hashCode(identifier); + } + + @Override + public Type type() { + return Type.floatType(); + } +} diff --git a/vadl-frontend/main/vadl/ast/nodes/RecursiveAstVisitor.java b/vadl-frontend/main/vadl/ast/nodes/RecursiveAstVisitor.java index 0d5cfdfa3..98a52299e 100644 --- a/vadl-frontend/main/vadl/ast/nodes/RecursiveAstVisitor.java +++ b/vadl-frontend/main/vadl/ast/nodes/RecursiveAstVisitor.java @@ -247,6 +247,14 @@ public Void visit(ExceptionDefinition definition) { return null; } + @Override + public Void visit(FloatTypeDefinition definition) { + beforeTravel(definition); + definition.forEachChild(this::travel); + afterTravel(definition); + return null; + } + @Override public Void visit(FormatDefinition definition) { beforeTravel(definition); diff --git a/vadl-frontend/main/vadl/ast/vadl.ATG b/vadl-frontend/main/vadl/ast/vadl.ATG index 352afcbed..2b54344e6 100644 --- a/vadl-frontend/main/vadl/ast/vadl.ATG +++ b/vadl-frontend/main/vadl/ast/vadl.ATG @@ -181,6 +181,7 @@ TOKENS FALSE = "false". FETCH = "fetch". FILE = "file". + FLOAT_TYPE = "float-type". FOLD = "fold". FOR = "for". FORALL = "forall". @@ -417,6 +418,7 @@ The #commonDefinitionList production rule lists (beside macro definitions) all c commonDefinition (. def = DUMMY_DEF; .) = constantDefinition + | floatTypeDefinition | formatDefinition | enumerationDefinition | usingDefinition @@ -516,6 +518,11 @@ elements. A single annotation is described in the #annotation production rule. expression (. def = new ConstantDefinition(id, type, expr, startLocation.join(lastTokenLoc())); .) . + floatTypeDefinition (. var startLocation = nextTokenLoc(); .) + = FLOAT_TYPE + identifierOrPlaceholder (. def = new FloatTypeDefinition(id, startLocation.join(lastTokenLoc())); .) + . + formatDefinition (. var startLoc = nextTokenLoc(); var fields = new ArrayList(); .) = FORMAT identifierOrPlaceholder diff --git a/vadl/main/resources/templates/iss/target/gen-arch/cpu.c b/vadl/main/resources/templates/iss/target/gen-arch/cpu.c index e361b7bfe..4a56be03b 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/cpu.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/cpu.c @@ -102,6 +102,9 @@ static void [(${gen_arch_lower})]_cpu_reset_hold(Object *obj, ResetType type) [# th:each="access : ${base_clear_cpu_accessors}"] [(${access.name})](env); [/] + // disable NaN propagation + set_default_nan_mode(1, &env->fp_status); + [(${reset})] } diff --git a/vadl/main/resources/templates/iss/target/gen-arch/cpu.h b/vadl/main/resources/templates/iss/target/gen-arch/cpu.h index 99034e623..993994048 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/cpu.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/cpu.h @@ -32,6 +32,8 @@ typedef struct CPUArchState { [# th:each="exc : ${exc_info.exceptions}"] [# th:each="p : ${exc.params}"] [(${p.c_type})] [(${p.name_in_cpu})]; [/][/] + + float_status fp_status; } CPU[(${gen_arch_upper})]State; diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index b0df5e67a..e42cf2bda 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -33,3 +33,8 @@ void helper_unsupported(CPU[(${gen_arch_upper})]State *env) { [(${instr})] [/] +// float helpers + +uint32_t helper_fadd_ieee32(CPU[(${gen_arch_upper})]State *env, uint32_t rs1, uint32_t rs2) { + return float32_add(rs1, rs2, &env->fp_status); +} diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.h b/vadl/main/resources/templates/iss/target/gen-arch/helper.h index 107003b8d..48df1583c 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.h @@ -9,4 +9,8 @@ DEF_HELPER_1(unsupported, noreturn, env) // helper definitions for instructions [# th:each="instr : ${instr_helper_defs}"] [(${instr})] -[/] \ No newline at end of file +[/] + +// float helpers + +DEF_HELPER_FLAGS_3(fadd_ieee32, TCG_CALL_NO_RWG, i32, env, i32, i32) \ No newline at end of file diff --git a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java index d06ce406a..afaf1fca5 100644 --- a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java +++ b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java @@ -810,6 +810,11 @@ public void handleCTO(BuiltInCall input) { throw graphError(input, "Normalization not yet implemented for this built-in"); } + @Override + public void handleFADD(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + @Override public void handleConcat(BuiltInCall input) { // do nothing (result is already fine) diff --git a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java index 6715c2da6..4eec79cd3 100644 --- a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java +++ b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java @@ -64,6 +64,7 @@ import vadl.iss.passes.tcg.lowering.nodes.TcgGenException; import vadl.iss.passes.tcg.lowering.nodes.TcgGottoTb; import vadl.iss.passes.tcg.lowering.nodes.TcgGvecOpNode; +import vadl.iss.passes.tcg.lowering.nodes.TcgHelperCall; import vadl.iss.passes.tcg.lowering.nodes.TcgLoadMemory; import vadl.iss.passes.tcg.lowering.nodes.TcgLookupAndGotoPtr; import vadl.iss.passes.tcg.lowering.nodes.TcgMovCondNode; @@ -641,7 +642,7 @@ void handle(ReadRegTensorNode toHandle) { /** * Handles the {@link IssLoadNode}, which was created from a {@link ReadMemNode} - * in the {@link vadl.iss.passes.IssMemoryAccessTransformationPass}. + * in the {@link vadl.iss.passes.common.IssMemoryAccessTransformationPass}. */ @Handler void handle(IssLoadNode toHandle) { @@ -694,7 +695,7 @@ void handle(IssRegBitfieldWriteNode toHandle) { /** * Handles the {@link IssStoreNode}, which was created from a {@link WriteMemNode} - * in the {@link vadl.iss.passes.IssMemoryAccessTransformationPass}. + * in the {@link vadl.iss.passes.common.IssMemoryAccessTransformationPass}. */ @Handler void handle(IssStoreNode toHandle) { @@ -949,7 +950,7 @@ void handle(InstructionWidthNode toHandle) { /** * Handles the {@link ReadMemNode}. Should be replaced by a {@link IssLoadNode} in the - * {@link vadl.iss.passes.IssMemoryAccessTransformationPass}. + * {@link vadl.iss.passes.common.IssMemoryAccessTransformationPass}. */ @Handler void handle(ReadMemNode toHandle) { @@ -958,7 +959,7 @@ void handle(ReadMemNode toHandle) { /** * Handles the {@link WriteMemNode}. Should be replaced by a {@link IssStoreNode} in the - * {@link vadl.iss.passes.IssMemoryAccessTransformationPass}. + * {@link vadl.iss.passes.common.IssMemoryAccessTransformationPass}. */ @Handler void handle(WriteMemNode toHandle) { @@ -1026,7 +1027,7 @@ void handle(LabelNode toHandle) { } /** - * The {@link IssGhostCastNode} is removed in the {@link vadl.iss.passes.IssTcgSchedulingPass} + * The {@link IssGhostCastNode} is removed in the {@link vadl.iss.passes.tcg.IssTcgSchedulingPass} * if it had been scheduled. * So it cannot occur during op lowering. */ @@ -1253,6 +1254,15 @@ class BuiltInTcgLoweringExecutor { ); }) + //// Float Arithmetic //// + + .set(BuiltInTable.FADD, (ctx) -> out( + new TcgHelperCall( + ctx.dest(), new NodeList<>(ctx.src(0), ctx.src(1)), true, + "fadd_ieee32" + ) + )) + .build(); } diff --git a/vadl/main/vadl/types/BuiltInTable.java b/vadl/main/vadl/types/BuiltInTable.java index f64f0e139..d2ed90cc3 100644 --- a/vadl/main/vadl/types/BuiltInTable.java +++ b/vadl/main/vadl/types/BuiltInTable.java @@ -1041,14 +1041,30 @@ public class BuiltInTable { ///// FLOAT ARITHMETIC ////// + /** + * {@code function fadd( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + */ + public static final BuiltIn FADD = + func("VADL::fadd", + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstTwoWithSameBitWidthsAndFrm(2) + .returnsFirstBitWidth(BitsType.class) + .build(); + // TODO: I think we want a status variant for float built-ins, similar to other built-ins. + // But how to handle these for ISS? + // When doing 64- or even 128-bit ops, returning the status from helpers is afaik not + // possible. So we'd need to store them in a reg or an env variable and load them later. + // But we have to make sure that other float ops are not scheduled in between! + /** + * {@code function fadds( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> + * ( Bits, FloatStatus ) } + */ public static final BuiltIn FADDS = - func("VADL::fadds", - Type.relation( - List.of(FloatType.class, FloatType.class, BitsType.class), StructType.class)) - .takesData(args -> args.get(0).bitWidth() == args.get(1).bitWidth() - && args.get(2).bitWidth() == 5) - .returnsFirstFloatAndStatus() + func("VADL::fadds", Type.relation( + List.of(BitsType.class, BitsType.class, BitsType.class), 1, StructType.class)) + .takesFirstTwoWithSameBitWidthsAndFrm(2) + .returnsFirstBitWidthAndFloatStatus() .build(); @@ -1202,7 +1218,7 @@ public class BuiltInTable { */ public static final BuiltIn LA_ID_IN = func("LaIdIn", null, - Type.relation(List.of(UIntType.class, StringType.class), true, BoolType.class)) + Type.relation(List.of(UIntType.class, StringType.class), true, 0, BoolType.class)) .takesDefault() .noCompute() .returns(Type.bool()) @@ -1216,7 +1232,7 @@ public class BuiltInTable { */ public static final BuiltIn LA_KIND_IN = func("LaKindIn", null, - Type.relation(List.of(UIntType.class, StringType.class), true, BoolType.class)) + Type.relation(List.of(UIntType.class, StringType.class), true, 0, BoolType.class)) .takesDefault() .noCompute() .returns(Type.bool()) @@ -1423,7 +1439,7 @@ private static BuiltIn instr(String name) { ); public static final List FLOAT_ARITHMETIC_BUILT_INS = List.of( - FADDS + FADD ); public static final List FUNCTION_BUILT_INS = List.of( @@ -1456,13 +1472,17 @@ private static BuiltIn instr(String name) { INSTRUCTION_WRITE ); + public static final List FLOAT_BUILT_INS = Stream.of( + FLOAT_ARITHMETIC_BUILT_INS.stream() + ).flatMap(s -> s).toList(); + public static final List BUILT_INS = Stream.of( ARITHMETIC_BUILT_INS.stream(), LOGICAL_BUILT_INS.stream(), COMPARISON_BUILT_INS.stream(), SHIFTING_BUILT_INS.stream(), BITWISE_COUNTING_BUILT_INS.stream(), - FLOAT_ARITHMETIC_BUILT_INS.stream(), + FLOAT_BUILT_INS.stream(), FUNCTION_BUILT_INS.stream(), ASM_PARSER_BUILT_INS_LIST.stream(), MICRO_ARCHITECTURE_BUILT_INS.stream() @@ -1844,6 +1864,15 @@ public BuiltInBuilder takesFirstTwoWithSameBitWidths() { return this; } + public BuiltInBuilder takesFirstTwoWithSameBitWidthsAndFrm(int roundingModeArgIdx) { + takesData((args) -> args.size() > roundingModeArgIdx + && args.get(0).bitWidth() == args.get(1).bitWidth() + && args.get(roundingModeArgIdx).bitWidth() == 3 + ); + this.hasSameBitWidth = true; + return this; + } + public BuiltInBuilder returns(Type returnType) { returns((args) -> returnType); return this; @@ -1877,9 +1906,9 @@ public BuiltInBuilder returnsFirstBitWidthAndStatus( return this; } - public BuiltInBuilder returnsFirstFloatAndStatus() { + public BuiltInBuilder returnsFirstBitWidthAndFloatStatus() { returnsFromFirstAsDataType((firstDataType) -> { - var valType = constructDataType(FloatType.class, firstDataType.bitWidth()); + var valType = constructDataType(BitsType.class, firstDataType.bitWidth()); Objects.requireNonNull(valType); return Type.struct( BUILTIN_RESULT, valType, diff --git a/vadl/main/vadl/types/FloatType.java b/vadl/main/vadl/types/FloatType.java index 47437891c..d903ae18b 100644 --- a/vadl/main/vadl/types/FloatType.java +++ b/vadl/main/vadl/types/FloatType.java @@ -16,52 +16,15 @@ package vadl.types; -import javax.annotation.CheckForNull; - /** - * An IEEE-754 32/64-bit float. + * A class that represents the VADL float type. */ -public class FloatType extends BitsType { - - /** - * The size of the float. - */ - public enum Size { - FP32(32), - FP64(64); - - final int bitWidth; - - Size(int bitWidth) { - this.bitWidth = bitWidth; - } - } - - protected final Size size; +public class FloatType extends Type { - protected FloatType(Size size) { - super(size.bitWidth); - this.size = size; - } + protected FloatType() { } @Override public String name() { - return "FP%s".formatted(size.bitWidth); - } - - @CheckForNull - @Override - public DataType fittingCppType() { - return null; - } - - @Override - public boolean equals(Object obj) { - return this.getClass() == obj.getClass() && this.size == ((FloatType) obj).size; - } - - @Override - public int hashCode() { - return super.hashCode(); + return "FloatType"; } } diff --git a/vadl/main/vadl/types/RelationType.java b/vadl/main/vadl/types/RelationType.java index 9585d6a3c..18f265885 100644 --- a/vadl/main/vadl/types/RelationType.java +++ b/vadl/main/vadl/types/RelationType.java @@ -18,6 +18,8 @@ import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; /** * Represents a relation type in VADL's type system. @@ -29,12 +31,15 @@ public class RelationType extends Type { private final List> argTypeClass; private final boolean hasVarArgs; + private final int floatTypeArgCount; private final Class resultTypeClass; protected RelationType(List> argTypes, boolean hasVarArgs, + int floatTypeArgCount, Class resultType) { this.argTypeClass = argTypes; this.hasVarArgs = hasVarArgs; + this.floatTypeArgCount = floatTypeArgCount; this.resultTypeClass = resultType; } @@ -46,6 +51,10 @@ public boolean hasVarArgs() { return hasVarArgs; } + public int floatTypeArgCount() { + return floatTypeArgCount; + } + public Class resultTypeClass() { return resultTypeClass; } @@ -58,4 +67,16 @@ public String name() { + ") -> " + resultTypeClass.getSimpleName(); } + + /** + * A readable representation of the type, with the {@link FloatType} arguments. + */ + public String nameWithFloatTypes() { + return "(" + + Stream.concat( + IntStream.range(0, floatTypeArgCount).mapToObj(i -> Type.floatType().name()), + argTypeClass.stream().map(Class::getSimpleName)).collect(Collectors.joining(", ")) + + ") -> " + + resultTypeClass.getSimpleName(); + } } diff --git a/vadl/main/vadl/types/Type.java b/vadl/main/vadl/types/Type.java index c71d0d661..1e727e7eb 100644 --- a/vadl/main/vadl/types/Type.java +++ b/vadl/main/vadl/types/Type.java @@ -120,35 +120,18 @@ public static UIntType unsignedInt(int bitWidth) { .computeIfAbsent(bitWidth, k -> new UIntType(bitWidth)); } - private static final HashMap floatTyps = new HashMap<>(); + private static @Nullable FloatType floatType = null; /** - * Retrieves the instance of FloatType with the specified size. + * Retrieves the instance of FloatType. * - * @param size the size of the FloatType object - * @return the FloatType object with the specified size + * @return the FloatType object */ - public static FloatType floatType(FloatType.Size size) { - return floatTyps - .computeIfAbsent(size, k -> new FloatType(size)); - } - - /** - * Retrieves the instance of FloatType with size 32. - * - * @return the FloatType object with size 32 - */ - public static FloatType float32() { - return floatType(FloatType.Size.FP32); - } - - /** - * Retrieves the instance of FloatType with size 64. - * - * @return the FloatType object with size 64 - */ - public static FloatType float64() { - return floatType(FloatType.Size.FP64); + public static FloatType floatType() { + if (floatType == null) { + floatType = new FloatType(); + } + return floatType; } /** @@ -303,7 +286,20 @@ public static StringType string() { */ public static RelationType relation(List> argTypes, Class returnType) { - return relation(argTypes, false, returnType); + return relation(argTypes, false, 0, returnType); + } + + /** + * Retrieves the generic relation type. + * + * @param argTypes the list of argument type classes + * @param returnType the return type class + * @return the RelationType instance + */ + public static RelationType relation(List> argTypes, + int floatTypeArgCount, + Class returnType) { + return relation(argTypes, false, floatTypeArgCount, returnType); } /** @@ -316,10 +312,11 @@ public static RelationType relation(List> argTypes, */ public static RelationType relation(List> argTypes, boolean hasVarArgs, + int floatTypeArgCount, Class returnType) { var hashCode = Objects.hash(argTypes, hasVarArgs, returnType); - return relationTypes - .computeIfAbsent(hashCode, k -> new RelationType(argTypes, hasVarArgs, returnType)); + return relationTypes.computeIfAbsent(hashCode, k -> + new RelationType(argTypes, hasVarArgs, floatTypeArgCount, returnType)); } /** @@ -329,7 +326,7 @@ public static RelationType relation(List> argTypes, * @return the RelationType instance */ public static RelationType relation(Class returnType) { - return relation(List.of(), false, returnType); + return relation(List.of(), false, 0, returnType); } /** @@ -355,7 +352,7 @@ public static RelationType relation(Class argType, public static RelationType relation(Class firstArg, Class secondArg, Class returnType) { - return relation(List.of(firstArg, secondArg), false, returnType); + return relation(List.of(firstArg, secondArg), false, 0, returnType); } private static final HashMap concreteRelationTypes = @@ -438,14 +435,6 @@ public static DataType constructDataType(Class typeClass, in return Type.signedInt(bitWidth); } else if (typeClass == UIntType.class) { return Type.unsignedInt(bitWidth); - } else if (typeClass == FloatType.class) { - if (bitWidth == FloatType.Size.FP32.bitWidth) { - return Type.float32(); - } else if (bitWidth == FloatType.Size.FP64.bitWidth) { - return Type.float64(); - } else { - return null; - } } else { return null; } @@ -476,6 +465,6 @@ public static GroupType group(Type elementType, UIntType lengthType, UIntType bi /// Some of them cannot be initialized like them since they require a size, like `SInt<16>` /// which is why they are named bases. public static final Set builtinTypeBases = Set.of( - "Bool", "String", "Bits", "UInt", "SInt", "FP32", "FP64", "Instruction", "FetchResult" + "Bool", "String", "Bits", "UInt", "SInt", "Instruction", "FetchResult" ); } diff --git a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java index 072457474..ea59f067c 100644 --- a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java @@ -190,6 +190,10 @@ default void handleCTZ(T input) { default void handleCTO(T input) { } + @Override + default void handleFADD(T input) { + } + @Override default void handleConcat(T input) { } diff --git a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java index 99be85bee..d84c71549 100644 --- a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java @@ -113,6 +113,8 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { handleCTZ(input); } else if (builtIn == BuiltInTable.CTO) { handleCTO(input); + } else if (builtIn == BuiltInTable.FADD) { + handleFADD(input); } else if (builtIn == BuiltInTable.CONCATENATE_BITS) { handleConcat(input); } else { @@ -205,6 +207,8 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { void handleCTO(T input); + void handleFADD(T input); + void handleConcat(T input); } diff --git a/vadl/main/vadl/viam/DefinitionVisitor.java b/vadl/main/vadl/viam/DefinitionVisitor.java index b5d0a6755..e81d540a7 100644 --- a/vadl/main/vadl/viam/DefinitionVisitor.java +++ b/vadl/main/vadl/viam/DefinitionVisitor.java @@ -44,6 +44,8 @@ public interface DefinitionVisitor { void visit(Group group); + void visit(FloatFormat floatFormat); + void visit(Format format); void visit(Format.Field formatField); @@ -198,6 +200,12 @@ public void visit(Group group) { afterTraversal(group); } + @Override + public void visit(FloatFormat floatFormat) { + beforeTraversal(floatFormat); + afterTraversal(floatFormat); + } + @Override public void visit(Format format) { beforeTraversal(format); @@ -483,6 +491,11 @@ public void visit(Group group) { } + @Override + public void visit(FloatFormat floatFormat) { + + } + @Override public void visit(Format format) { diff --git a/vadl/main/vadl/viam/FloatFormat.java b/vadl/main/vadl/viam/FloatFormat.java new file mode 100644 index 000000000..083e3e7c5 --- /dev/null +++ b/vadl/main/vadl/viam/FloatFormat.java @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.viam; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import vadl.types.Type; + +/** + * Describes a float format. This covers bit-size, encoding and interpretation. + */ +public class FloatFormat extends Definition implements DefProp.WithType { + + private int size = 0; + + @Nullable + private Constant canonicalSNaN = null; + @Nullable + private Constant canonicalQNaN = null; + + public FloatFormat(Identifier identifier) { + super(identifier); + } + + public void setSize(int size) { + this.size = size; + } + + public void setCanonicalSNaN(@CheckForNull Constant canonicalSNaN) { + this.canonicalSNaN = canonicalSNaN; + } + + public void setCanonicalQNaN(@CheckForNull Constant canonicalQNaN) { + this.canonicalQNaN = canonicalQNaN; + } + + /** + * The bit-size of the float format. + */ + public int size() { + return size; + } + + /** + * The canonical signaling NaN encoding of the float format. + */ + @Nullable + public Constant canonicalSNaN() { + return canonicalSNaN; + } + + /** + * The canonical quiet NaN encoding of the float format. + */ + @Nullable + public Constant canonicalQNaN() { + return canonicalQNaN; + } + + @Override + public Type type() { + return Type.floatType(); + } + + @Override + public void accept(DefinitionVisitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return identifier.simpleName(); + } +} diff --git a/vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java b/vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java new file mode 100644 index 000000000..f3bf7a422 --- /dev/null +++ b/vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText : © 2025 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.viam.graph.dependency; + +import static java.util.Collections.reverse; + +import java.util.List; +import vadl.javaannotations.viam.DataValue; +import vadl.types.BuiltInTable; +import vadl.types.BuiltInTable.BuiltIn; +import vadl.types.FloatType; +import vadl.types.Type; +import vadl.viam.FloatFormat; +import vadl.viam.graph.Canonicalizable; +import vadl.viam.graph.GraphNodeVisitor; +import vadl.viam.graph.Node; +import vadl.viam.graph.NodeList; + +/** + * Represents a function call to a VADL float built-in. + * It holds a {@link BuiltIn} function from the {@link BuiltInTable} and + * extends {@link BuiltInCall}. + * + * @see BuiltInCall + * @see BuiltInTable + * @see AbstractFunctionCallNode + */ +public class FloatBuiltInCall extends BuiltInCall { + + @DataValue + protected List formats; + + public FloatBuiltInCall(BuiltIn builtIn, NodeList args, + List formats, Type type) { + super(builtIn, args, type); + this.formats = formats; + } + + public List formats() { + return formats; + } + + @Override + public void verifyState() { + super.verifyState(); + ensure(builtIn.signature().floatTypeArgCount() == formats().size(), + "Number of float types must match, %s vs %s", + builtIn.signature().floatTypeArgCount(), formats().size()); + } + + @Override + public ExpressionNode copy() { + return new FloatBuiltInCall(builtIn, + new NodeList<>(arguments().stream().map(ExpressionNode::copy).toList()), + formats(), + type()); + } + + @Override + public Node shallowCopy() { + return new FloatBuiltInCall(builtIn, args, formats(), type()); + } + + + @Override + protected void collectData(List collection) { + super.collectData(collection); + collection.add(formats); + } + + @Override + public void prettyPrint(StringBuilder sb) { + sb.append(builtIn.name()); + sb.append("("); + + for (int i = 0; i < args.size(); i++) { + if (i > 0) { + sb.append(", "); + } + args.get(i).prettyPrint(sb); + } + + for (int i = 0; i < formats.size(); i++) { + if (i > 0 || !args.isEmpty()) { + sb.append(", "); + } + formats.get(i).simpleName(); + } + + sb.append(")"); + } +} From 83babe1337e9ab567f207fe625d157a8d2a56eff Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Mon, 20 Jul 2026 19:05:49 +0200 Subject: [PATCH 03/11] wip: Add float config annotations --- sys/risc-v/rv64csr.vadl | 12 +- sys/risc-v/rv64f.vadl | 42 +++--- .../main/vadl/ast/AnnotationTable.java | 121 ++++++++++++++++++ vadl-frontend/main/vadl/ast/SymbolTable.java | 11 ++ vadl-frontend/main/vadl/ast/TypeChecker.java | 12 +- vadl-frontend/main/vadl/ast/ViamLowering.java | 2 + .../templates/iss/target/gen-arch/cpu.c | 8 +- .../templates/iss/target/gen-arch/cpu.h | 5 +- .../templates/iss/target/gen-arch/helper.c | 45 ++++++- .../templates/iss/target/gen-arch/helper.h | 3 +- .../vadl/iss/passes/extensions/RegInfo.java | 17 +++ .../tcg/lowering/TcgOpLoweringPass.java | 18 ++- .../template/IssTemplateRenderingPass.java | 21 +++ vadl/main/vadl/viam/DefinitionVisitor.java | 1 + vadl/main/vadl/viam/FloatExceptionFlag.java | 56 ++++++++ vadl/main/vadl/viam/FloatFormat.java | 68 +++++++++- .../vadl/viam/InstructionSetArchitecture.java | 11 ++ .../viam/annotations/FloatFlagAnnotation.java | 63 +++++++++ .../annotations/RegisterSliceAnnotation.java | 118 +++++++++++++++++ .../TbStateRegisterAnnotation.java | 93 +------------- .../IssTensorAssignmentToForallPassTest.java | 1 + .../CanonicalizationPassTest.java | 1 + 22 files changed, 594 insertions(+), 135 deletions(-) create mode 100644 vadl/main/vadl/viam/FloatExceptionFlag.java create mode 100644 vadl/main/vadl/viam/annotations/FloatFlagAnnotation.java create mode 100644 vadl/main/vadl/viam/annotations/RegisterSliceAnnotation.java diff --git a/sys/risc-v/rv64csr.vadl b/sys/risc-v/rv64csr.vadl index 4eb893ce6..6701aeeec 100644 --- a/sys/risc-v/rv64csr.vadl +++ b/sys/risc-v/rv64csr.vadl @@ -4,7 +4,8 @@ import rv3264im::{RV3264Base, RV3264M} with ("ArchSize=Arch64") instruction set architecture RV64IZicsr extending RV3264Base = { enumeration CsrDef : Bits<12> = // defined control and status register indices - { mstatus = 0x300 // 768 Machine STATUS + { fcsr = 0x003 // 003 Floating-Point Control and Status Register (frm + fflags) + , mstatus = 0x300 // 768 Machine STATUS , misa = 0x301 // 769 Machine ISA , mie = 0x304 // 772 Machine Interrupt Enable register , mtvec = 0x305 // 773 Machine Trap VECtor base address @@ -20,7 +21,8 @@ instruction set architecture RV64IZicsr extending RV3264Base = { using CsrImplIndex = Bits // index type for implemented CSR registers enumeration CsrImpl : CsrImplIndex = // implemented control and status register indices - { mstatus // 0x300 Machine STATUS + { fcsr // 0x003 003 Floating-Point Control and Status Register (frm + fflags) + , mstatus // 0x300 Machine STATUS , misa // 0x301 Machine ISA , mie // 0x304 Machine Interrupt Enable , mtvec // 0x305 Machine Trap VECtor base address @@ -35,7 +37,8 @@ instruction set architecture RV64IZicsr extending RV3264Base = { function CsrDefToImpl (csr : Bits<12>) -> CsrImplIndex = // map defined CSR index to implemented CSR index match csr with - { CsrDef::mstatus => CsrImpl::mstatus // 0x300 Machine STATUS + { CsrDef::fcsr => CsrImpl::fcsr // 0x003 Floating-Point Control and Status Register (frm + fflags) + , CsrDef::mstatus => CsrImpl::mstatus // 0x300 Machine STATUS , CsrDef::misa => CsrImpl::misa // 0x301 Machine ISA , CsrDef::mie => CsrImpl::mie // 0x304 Machine Interrupt Enable , CsrDef::mtvec => CsrImpl::mtvec // 0x305 Machine Trap VECtor base address @@ -50,7 +53,8 @@ instruction set architecture RV64IZicsr extending RV3264Base = { function CsrName(index : Bits<12>) -> String = match index with - { CsrDef::mstatus => "mstatus" // Machine STATUS + { CsrDef::fcsr => "fcsr" // Floating-Point Control and Status Register (frm + fflags) + , CsrDef::mstatus => "mstatus" // Machine STATUS , CsrDef::misa => "misa" // Machine ISA , CsrDef::mie => "mie" // Machine Interrupt Enable , CsrDef::mtvec => "mtvec" // Machine Trap VECtor base address diff --git a/sys/risc-v/rv64f.vadl b/sys/risc-v/rv64f.vadl index 555e29b89..c75a47e3a 100644 --- a/sys/risc-v/rv64f.vadl +++ b/sys/risc-v/rv64f.vadl @@ -29,22 +29,22 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { register F : Index -> FRegs + //[ sticky float flag : invalid, nv ] + //[ sticky float flag : div_by_zero, dz ] + //[ sticky float flag : overflow, of ] + //[ sticky float flag : underflow, uf ] + [ sticky float flag : inexact, nx ] register FCSR : FCsrFormat + //alias register fcsr : FCsrFormat = CSR(CsrDefToImpl(CsrDef::fcsr)) - //[ sticky float flag : invalid, nv ] - //[ sticky float flag : divide_by_zero, dz ] - //[ sticky float flag : overflow, of ] - //[ sticky float flag : underflow, uf ] - //[ sticky float flag : inexact, nx ] format FCsrFormat : Bits<32> = { reserved [31..8] - , frm [7..5] // Rounding mode - , fflags [4..0] // Float exception flags - , nv = fflags(4) // Float exception flag: Invalid operation - , dz = fflags(3) // Float exception flag: Division by zero - , of = fflags(2) // Float exception flag: Overflow - , uf = fflags(1) // Float exception flag: Underflow - , nx = fflags(0) // Float exception flag: Inexact + , frm [7..5] // Rounding mode + , nv [4] // Float exception flag: Invalid operation + , dz [3] // Float exception flag: Division by zero + , of [2] // Float exception flag: Overflow + , uf [1] // Float exception flag: Underflow + , nx [0] // Float exception flag: Inexact } // rounding modes @@ -124,14 +124,14 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { record FSizeRec (suffix : Id, iSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, fTy : Id, size : Int) - //[ canonical sNaN : 0x7fa00000 ] - //[ canonical qNaN : 0x7fc00000 ] - //[ IEEE float : 32 ] + [ canonical sNaN : 0x7fa00000 ] + [ canonical qNaN : 0x7fc00000 ] + [ IEEE : 32 ] float-type IEEE32 - //[ canonical sNaN : 0x7fa00000'00000000 ] - //[ canonical qNaN : 0x7fc00000'00000000 ] - //[ IEEE float : 64 ] + [ canonical sNaN : 0x7fa00000'00000000 ] + [ canonical qNaN : 0x7fc00000'00000000 ] + [ IEEE : 64 ] float-type IEEE64 // TODO: IEEE16 and IEEE128 are not yet implemented @@ -247,9 +247,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { AsStr($name, ".", $iSize.iSuffix, $u("U" ; ""), ".", $fSize.suffix) ; $rm ; $fSize.fmt ; $funct5 ; // TODO: we must make sure the builtin understands what type to convert to - let result, flags = $fun($fSize.fTy, F(rs1) as $fSize.ty, $rm) in { + let result = $fun($fSize.fTy, F(rs1) as $fSize.ty, $rm) in { X(rd) := result as $u(iSize.uTy ; iSize.sTy) - FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } ) ; $rs2 ) } @@ -290,9 +289,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { instruction AsId($name, $size.suffix) : R4type = - let result, flags = $fun($size.fTy, F(rs1), F(rs2), F(rs3), $rm) in { + let result = $fun($size.fTy, F(rs1), F(rs2), F(rs3), $rm) in { F(rd) := $NaNBox($size ; result) - FCSR.fflags := (flags.nv, flags.dz, flags.of, flags.uf, flags.nx) } encoding AsId($name, $size.suffix) = {opcode = 0b100'0011, funct3 = $rm, fmt = $size.fmt} assembly AsId($name, $size.suffix) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($rm)) diff --git a/vadl-frontend/main/vadl/ast/AnnotationTable.java b/vadl-frontend/main/vadl/ast/AnnotationTable.java index 7659bd8dd..b2e42dddf 100644 --- a/vadl-frontend/main/vadl/ast/AnnotationTable.java +++ b/vadl-frontend/main/vadl/ast/AnnotationTable.java @@ -24,6 +24,7 @@ import com.google.errorprone.annotations.concurrent.LazyInit; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -47,6 +48,7 @@ import vadl.ast.nodes.DerivedFormatField; import vadl.ast.nodes.EncodingDefinition; import vadl.ast.nodes.Expr; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.FormatField; import vadl.ast.nodes.GroupDefinition; import vadl.ast.nodes.Identifier; @@ -77,6 +79,8 @@ import vadl.viam.Counter; import vadl.viam.Encoding; import vadl.viam.Endianness; +import vadl.viam.FloatExceptionFlag; +import vadl.viam.FloatFormat; import vadl.viam.Format; import vadl.viam.Group; import vadl.viam.Instruction; @@ -330,6 +334,59 @@ public class AnnotationTable { }) .build(); + /// FLOAT RELATED /// + + annotationOn(FloatTypeDefinition.class, "IEEE", ConstantAnnotation::new) + .applyViam((def, annotation, lowering) -> { + var encoding = FloatFormat.Encoding.ieee(annotation.constant.value().intValue()); + ensure(encoding != null, + () -> error("Invalid IEEE encoding size", annotation) + .description("The following sizes are supported: %s", + Arrays.stream(FloatFormat.Encoding.values()) + .map(e -> Integer.toString(e.size)).collect(Collectors.joining(", "))) + ); + ((FloatFormat) def).setEncoding(encoding); + }).build(); + + annotationOn(FloatTypeDefinition.class, "canonical sNaN", ConstantAnnotation::new) + .applyViam((def, annotation, lowering) -> + ((FloatFormat) def).setCanonicalSNaN(annotation.constant.toViamConstant())).build(); + + annotationOn(FloatTypeDefinition.class, "canonical qNaN", ConstantAnnotation::new) + .applyViam((def, annotation, lowering) -> + ((FloatFormat) def).setCanonicalQNaN(annotation.constant.toViamConstant())).build(); + + TriConsumer applyViamFloatFlag; + applyViamFloatFlag = (reg, annotation, sticky) -> { + var idx = annotation.index; + if (reg.hasAnnotation(vadl.viam.annotations.FloatFlagAnnotation.class)) { + var ann = reg.expectAnnotation(vadl.viam.annotations.FloatFlagAnnotation.class); + var flag = ann.get(idx); + ensure(flag == null, () -> error( + "Bit already mapped as " + (ann.isSticky(idx) ? "" : "non ") + + "sticky " + requireNonNull(flag).name + " flag", + annotation + )); + ann.set(idx, sticky, annotation.flag); + } else { + var ann = new vadl.viam.annotations.FloatFlagAnnotation(); + ann.set(idx, sticky, annotation.flag); + reg.addAnnotation(ann); + } + }; + + annotationOn(RegisterDefinition.class, "float flag", FloatFlagAnnotation::new) + .check((def, annotation, lowering) -> annotation.typeCheckTarget(def)) + .applyViam((def, annotation, lowering) -> + applyViamFloatFlag.accept((RegisterTensor) def, annotation, false)) + .build(); + + annotationOn(RegisterDefinition.class, "sticky float flag", FloatFlagAnnotation::new) + .check((def, annotation, lowering) -> annotation.typeCheckTarget(def)) + .applyViam((def, annotation, lowering) -> + applyViamFloatFlag.accept((RegisterTensor) def, annotation, true)) + .build(); + /// PROCESSOR RELATED /// annotationOn(ProcessorDefinition.class, "htif", EnableAnnotation::new) @@ -1103,6 +1160,70 @@ public String usageString() { } } +class FloatFlagAnnotation extends Annotation { + + @LazyInit + FloatExceptionFlag flag; + + @LazyInit + Identifier field; + + @LazyInit + int index; + + @Override + void resolveName(AnnotationDefinition definition, SymbolTable.SymbolResolver resolver) { + } + + @Override + void typeCheck(AnnotationDefinition definition, TypeChecker typeChecker) { + verifyValuesCnt(definition, 2); + definition.values.forEach(def -> { + Diagnostic.ensure(def instanceof Identifier, () -> error("Invalid annotation value", def) + .description("An identifier was expected.") + ); + }); + var flagName = ((Identifier) definition.values.get(0)).name; + flag = FloatExceptionFlag.from(flagName).orElseThrow(() -> + error("Invalid float flag", definition) + .description("Given flag is %s, but must be one of %s", flagName, + Arrays.stream(FloatExceptionFlag.values()).map(f -> f.name) + .collect(Collectors.joining(", ")) + ).build() + ); + field = (Identifier) definition.values.get(1); + } + + void typeCheckTarget(TypedNode target) { + Diagnostic.ensure(target.type() instanceof FormatType, + () -> error("Annotation target has invalid type", this).description(""" + Float flag annotation can only be applied to \ + register definitions with a format type""")); + + var format = ((FormatType) target.type()).format; + Function errBuilder = (String err) -> + error(err, field).description("Must be one of: %s", + format.fields.stream() + .filter(f -> !(f instanceof DerivedFormatField)) + .map(f -> f.identifier().name) + .collect(Collectors.joining(", ")) + ); + Diagnostic.ensure(format.hasField(field.name), + () -> errBuilder.apply("Unknown field name")); + Diagnostic.ensure(!(format.getField(field.name) instanceof DerivedFormatField), + () -> errBuilder.apply("Cannot annotate derived field")); + var range = requireNonNull(format.getFieldRange(field.name)); + Diagnostic.ensure(range.from() == range.to(), () -> + error("Float flag can only be one bit", field)); + index = range.from(); + } + + @Override + public String usageString() { + return "[ " + name + " : , ]"; + } +} + /** * An annotation that can be applied to anything that has a type which is a {@link BitsType}. * If the target's type is a {@link FormatType}, then this annotation can be used to reference diff --git a/vadl-frontend/main/vadl/ast/SymbolTable.java b/vadl-frontend/main/vadl/ast/SymbolTable.java index 74e1fa18c..8cefe39da 100644 --- a/vadl-frontend/main/vadl/ast/SymbolTable.java +++ b/vadl-frontend/main/vadl/ast/SymbolTable.java @@ -52,6 +52,7 @@ import vadl.ast.nodes.ExistsInExpr; import vadl.ast.nodes.ExistsInThenExpr; import vadl.ast.nodes.Expr; +import vadl.ast.nodes.FloatTypeDefinition; import vadl.ast.nodes.ForallExpr; import vadl.ast.nodes.ForallStatement; import vadl.ast.nodes.ForallThenExpr; @@ -1178,6 +1179,16 @@ public Void visit(InstructionSetDefinition definition) { return null; } + @Override + public Void visit(FloatTypeDefinition definition) { + beforeTravel(definition); + // FloatTypeDefinition has no @Child fields, so it's not in NodeChildrenRegistry, + // and we need to manually visit annotations + definition.annotations.forEach(this::travel); + afterTravel(definition); + return null; + } + @Override public Void visit(InstructionDefinition definition) { // Import all symbols from the format. diff --git a/vadl-frontend/main/vadl/ast/TypeChecker.java b/vadl-frontend/main/vadl/ast/TypeChecker.java index 8effef5a4..759b4be1a 100644 --- a/vadl-frontend/main/vadl/ast/TypeChecker.java +++ b/vadl-frontend/main/vadl/ast/TypeChecker.java @@ -1234,13 +1234,13 @@ private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, Li } var ftArgCnt = builtIn.signature().floatTypeArgCount(); - args = args.stream().skip(ftArgCnt).toList(); + var normalArgs = args.stream().skip(ftArgCnt).toList(); - var argTypes = args.stream().map(Expr::type).toList(); + var argTypes = normalArgs.stream().map(Expr::type).toList(); var areAllConst = argTypes.stream().allMatch(ConstantType.class::isInstance); if (areAllConst) { var type = constantEvaluator - .evalBuiltin(builtIn, args.stream().map(constantEvaluator::eval).toList(), location) + .evalBuiltin(builtIn, normalArgs.stream().map(constantEvaluator::eval).toList(), location) .type(); return new BuiltInCheckResult(null, type); } @@ -1256,10 +1256,10 @@ private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, Li // Inject implicit casts for constant types // NOTE: There might be functions that operate on bit patterns where this implicit cast might // not be intended and should be disallowed. - args = Streams.zip(args.stream(), declaredTypes, TypeChecker::wrapImplicitCastConstToTypeClass) - .toList(); + normalArgs = Streams.zip(normalArgs.stream(), declaredTypes, + TypeChecker::wrapImplicitCastConstToTypeClass).toList(); var originalArgTypes = argTypes; - argTypes = args.stream().map(Expr::type).toList(); + argTypes = normalArgs.stream().map(Expr::type).toList(); var ftArgs = args.stream().limit(ftArgCnt).toList(); var ftTypes = ftArgs.stream().map(Expr::type).toList(); diff --git a/vadl-frontend/main/vadl/ast/ViamLowering.java b/vadl-frontend/main/vadl/ast/ViamLowering.java index 4a9a0b56e..b8861c13b 100644 --- a/vadl-frontend/main/vadl/ast/ViamLowering.java +++ b/vadl-frontend/main/vadl/ast/ViamLowering.java @@ -1665,6 +1665,7 @@ private InstructionSetArchitecture visitAndMergeIsa(InstructionSetDefinition def mergedDef.definitions.stream().map(this::fetch).flatMap(Optional::stream) .toList(); var formats = filterAndCastToInstance(allDefinitions, Format.class); + var floatFormats = filterAndCastToInstance(allDefinitions, FloatFormat.class); var functions = filterAndCastToInstance(allDefinitions, Function.class); var operations = filterAndCastToInstance(allDefinitions, Operation.class); var relocations = filterAndCastToInstance(allDefinitions, Relocation.class); @@ -1709,6 +1710,7 @@ private InstructionSetArchitecture visitAndMergeIsa(InstructionSetDefinition def programCounter, memories, artificialResources, + floatFormats, group ); diff --git a/vadl/main/resources/templates/iss/target/gen-arch/cpu.c b/vadl/main/resources/templates/iss/target/gen-arch/cpu.c index 4a56be03b..d5ab192a3 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/cpu.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/cpu.c @@ -8,6 +8,7 @@ #include "trace.h" #include "tcg/debug-assert.h" #include "hw/qdev-properties.h" +#include "fpu/softfloat-helpers.h" #include "vadl-builtins.h" #include "vadl-iss-builtins.h" @@ -100,10 +101,11 @@ static void [(${gen_arch_lower})]_cpu_reset_hold(Object *obj, ResetType type) } [# th:each="access : ${base_clear_cpu_accessors}"] - [(${access.name})](env); [/] + [(${access.name})](env);[/] - // disable NaN propagation - set_default_nan_mode(1, &env->fp_status); + // TODO: this disables nan-propagation. but this should be specified per-instruction + [# th:each="fmt : ${float_formats}"] + set_default_nan_mode(1, &env->fp_status_[(${fmt.name})]);[/] [(${reset})] } diff --git a/vadl/main/resources/templates/iss/target/gen-arch/cpu.h b/vadl/main/resources/templates/iss/target/gen-arch/cpu.h index 993994048..798c6f47b 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/cpu.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/cpu.h @@ -4,6 +4,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" #include "qemu/typedefs.h" +#include "qemu/cpu-float.h" #include "cpu-bits.h" #define CPU_RESOLVING_TYPE TYPE_[(${gen_arch_upper})]_CPU @@ -33,7 +34,9 @@ typedef struct CPUArchState { [(${p.c_type})] [(${p.name_in_cpu})]; [/][/] - float_status fp_status; + [# th:each="fmt : ${float_formats}"] + float_status fp_status_[(${fmt.name})];[/] + } CPU[(${gen_arch_upper})]State; diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index e42cf2bda..02f05cf16 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -6,6 +6,7 @@ #include "qemu/log-for-trace.h" #include "qemu/qemu-print.h" #include "cpu-bits.h" +#include "fpu/softfloat.h" #include "vadl-builtins.h" #include "vadl-iss-builtins.h" @@ -35,6 +36,46 @@ void helper_unsupported(CPU[(${gen_arch_upper})]State *env) { // float helpers -uint32_t helper_fadd_ieee32(CPU[(${gen_arch_upper})]State *env, uint32_t rs1, uint32_t rs2) { - return float32_add(rs1, rs2, &env->fp_status); +void prep_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status *s) { + // DEV NOTE: this is generated from float flag annotations + uint16_t flags = 0xffff; + // un-set non sticky flags + [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.non_sticky_fe_flags}"] + flags &= ~(1 << [(${flag.flag_idx})]);[/][/] + // un-set sticky flags that are not set + [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.sticky_fe_flags}"] + flags &= ~((1 - ((env->[(${reg.name_lower})] >> [(${flag.idx})]) & 1)) << [(${flag.flag_idx})]);[/][/] + set_float_exception_flags(flags, s); } + +void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status *s) { + // DEV NOTE: for now we write directly to the flags register. This means that the helper is not pure and + // thus slower. In the future, we should optimize this. + uint16_t flags = get_float_exception_flags(s); + [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.sticky_fe_flags}"] + env->[(${reg.name_lower})] |= (flags >> [(${flag.flag_idx})] & 1) << [(${flag.idx})];[/][/] + [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.non_sticky_fe_flags}"] + env->[(${reg.name_lower})] &= ~((1 - (flags >> [(${flag.flag_idx})] & 1)) << [(${flag.idx})]);[/][/] +} + +[# th:each="size : ${float_ieee_sizes}"] +typedef uint[(${size})]_t (*f[(${size})]_fn)(uint[(${size})]_t a, uint[(${size})]_t b, float_status *s); +uint[(${size})]_t f[(${size})]_fn_with_fe_flags(CPU[(${gen_arch_upper})]State *env, f[(${size})]_fn fn, float_status *s, uint[(${size})]_t rs1, uint[(${size})]_t rs2) { + // DEV NOTE: flag stuff can be simplified when no flags present. But is that necessary? The flag functions + // should set all flags, if none are present in the spec. + // DEV NOTE: lets have a separate float_status for each float-type in the spec. but some things (e.g. rounding mode) + // are specified per call, so the float_status will be modified here either way (also because of flags). + // We could scratch that and rebuild a float_status from scratch every time -> env not affected. + prep_float_status_fe_flags(env, s); + uint[(${size})]_t result = fn(rs1, rs2, s); + set_float_status_fe_flags(env, s); + return result; +} +[/] + +[# th:each="fmt : ${float_formats}"] +uint[(${fmt.bit_size})]_t helper_fadd_[(${fmt.name})](CPU[(${gen_arch_upper})]State *env, uint[(${fmt.bit_size})]_t rs1, uint[(${fmt.bit_size})]_t rs2) { + // TODO: float[(${fmt.bit_size})]_add can only be used for ieee formats. other formats will need other functions + return f[(${fmt.bit_size})]_fn_with_fe_flags(env, float[(${fmt.bit_size})]_add, &env->fp_status_[(${fmt.name})], rs1, rs2); +} +[/] diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.h b/vadl/main/resources/templates/iss/target/gen-arch/helper.h index 48df1583c..04ceac7fa 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.h @@ -13,4 +13,5 @@ DEF_HELPER_1(unsupported, noreturn, env) // float helpers -DEF_HELPER_FLAGS_3(fadd_ieee32, TCG_CALL_NO_RWG, i32, env, i32, i32) \ No newline at end of file +[# th:each="fmt : ${float_formats}"] +DEF_HELPER_FLAGS_3(fadd_[(${fmt.name})], 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})])[/] diff --git a/vadl/main/vadl/iss/passes/extensions/RegInfo.java b/vadl/main/vadl/iss/passes/extensions/RegInfo.java index cf2e9582b..2e684dd03 100644 --- a/vadl/main/vadl/iss/passes/extensions/RegInfo.java +++ b/vadl/main/vadl/iss/passes/extensions/RegInfo.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.stream.IntStream; import javax.annotation.Nullable; import vadl.configuration.IssConfiguration; @@ -38,8 +39,10 @@ import vadl.viam.Constant; import vadl.viam.Definition; import vadl.viam.DefinitionExtension; +import vadl.viam.FloatExceptionFlag; import vadl.viam.RegisterResource; import vadl.viam.RegisterTensor; +import vadl.viam.annotations.FloatFlagAnnotation; import vadl.viam.annotations.TbStateRegisterAnnotation; import vadl.viam.graph.Node; import vadl.viam.graph.dependency.ConstantNode; @@ -186,6 +189,18 @@ private Map slicePart(int lsb, int msb) { ); } + private List> feFlags( + Function> flags) { + if (!reg().hasAnnotation(FloatFlagAnnotation.class)) { + return List.of(); + } + var ann = reg().expectAnnotation(FloatFlagAnnotation.class); + return flags.apply(ann).entrySet().stream().map(e -> Map.of( + "idx", Integer.toString(e.getKey()), + "flag_idx", Integer.toString(e.getValue().qemuFlagOffset) + )).toList(); + } + /** * Returns the execution class used for backend selection. */ @@ -291,6 +306,8 @@ public Map renderObj() { renderObj.put("is_gvec_capable", isGvecCapable()); renderObj.put("is_tb_state", isTbState()); renderObj.put("tb_state_parts", tbStateParts()); + renderObj.put("sticky_fe_flags", feFlags(FloatFlagAnnotation::stickyFlags)); + renderObj.put("non_sticky_fe_flags", feFlags(FloatFlagAnnotation::nonStickyFlags)); renderObj.put("exec_class", execClass().name()); renderObj.put("constraints", renderConstraints(dims)); renderObj.put("getter_params", renderParamsComma); diff --git a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java index 4eec79cd3..939fa2768 100644 --- a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java +++ b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java @@ -95,6 +95,7 @@ import vadl.utils.GraphUtils; import vadl.viam.Constant; import vadl.viam.ExceptionDef; +import vadl.viam.FloatFormat; import vadl.viam.Specification; import vadl.viam.graph.Graph; import vadl.viam.graph.NodeList; @@ -110,6 +111,7 @@ import vadl.viam.graph.dependency.ConstantNode; import vadl.viam.graph.dependency.DependencyNode; import vadl.viam.graph.dependency.DynSliceNode; +import vadl.viam.graph.dependency.FloatBuiltInCall; import vadl.viam.graph.dependency.FoldNode; import vadl.viam.graph.dependency.ForIdxNode; import vadl.viam.graph.dependency.FuncCallNode; @@ -1259,7 +1261,7 @@ class BuiltInTcgLoweringExecutor { .set(BuiltInTable.FADD, (ctx) -> out( new TcgHelperCall( ctx.dest(), new NodeList<>(ctx.src(0), ctx.src(1)), true, - "fadd_ieee32" + "fadd_" + ctx.floatFormat(0).nameLower() ) )) @@ -1325,6 +1327,20 @@ private TcgVRefNode src(int index) { return assignments.singleDestOf(arg); } + /** + * Retrieves the float format of a float built-in call with the given index. + * + * @param index The index of the float format. + * @return The float format. + */ + private FloatFormat floatFormat(int index) { + call.ensure(call instanceof FloatBuiltInCall, "Call is not float built-in"); + var floatCall = (FloatBuiltInCall) call; + floatCall.ensure(floatCall.formats().size() > index, + "Tried to access float format %s", index); + return floatCall.formats().get(index); + } + /** * Returns a temporary tcgV ref node for the given local index id. * If a temp for {@code i} does not exist yet, it creates one and returns it. diff --git a/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java b/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java index c848ac9d5..47a3fcc8c 100644 --- a/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java +++ b/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java @@ -16,6 +16,7 @@ package vadl.iss.template; +import static java.util.Objects.requireNonNull; import static vadl.error.Diagnostic.ensure; import static vadl.error.Diagnostic.error; import static vadl.iss.template.IssRenderUtils.mapRegTensors; @@ -23,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import javax.annotation.Nullable; import org.apache.commons.io.FilenameUtils; @@ -37,6 +39,7 @@ import vadl.pass.PassResults; import vadl.template.AbstractTemplateRenderingPass; import vadl.viam.Endianness; +import vadl.viam.FloatFormat; import vadl.viam.Memory; import vadl.viam.Specification; @@ -130,6 +133,8 @@ protected Map createVariables(PassResults passResults, vars.put("gen_machine_upper", configuration().machineName().toUpperCase()); vars.put("gen_machine_lower", configuration().machineName().toLowerCase()); vars.put("register_tensors", mapRegTensors(specification)); + vars.put("float_formats", getFloatFormats(specification)); + vars.put("float_ieee_sizes", getFloatIEEESizes(specification)); vars.put("pc_info", getPcInfo(specification)); vars.put("target_size", configuration().targetSize().width); vars.put("mem_regions", memRegions(specification)); @@ -148,6 +153,22 @@ private ExceptionInfo getExceptionInfo(Specification viam) { return viam.processor().get().isa().expectExtension(ExceptionInfo.class); } + private List> getFloatFormats(Specification viam) { + return viam.isa().get().ownFloatFormats().stream().map(fmt -> Map.of( + "name", fmt.nameLower(), + "bit_size", Integer.toString(requireNonNull(fmt.encoding()).size) + )).toList(); + } + + private List getFloatIEEESizes(Specification viam) { + return viam.isa().get().ownFloatFormats().stream() + .map(fmt -> requireNonNull(fmt.encoding())) + .filter(e -> e.ieee).map(e -> e.size) + .distinct() + .map(size -> Integer.toString(size)) + .toList(); + } + private Map getPcInfo(Specification viam) { var pc = viam.processor().get().isa().pc(); if (pc == null) { diff --git a/vadl/main/vadl/viam/DefinitionVisitor.java b/vadl/main/vadl/viam/DefinitionVisitor.java index e81d540a7..790b74af2 100644 --- a/vadl/main/vadl/viam/DefinitionVisitor.java +++ b/vadl/main/vadl/viam/DefinitionVisitor.java @@ -140,6 +140,7 @@ public void visit(InstructionSetArchitecture isa) { isa.ownMemories().forEach(e -> e.accept(this)); isa.artificialResources().forEach(e -> e.accept(this)); isa.ownInstructions().forEach(e -> e.accept(this)); + isa.ownFloatFormats().forEach(e -> e.accept(this)); isa.ownPseudoInstructions().forEach(e -> e.accept(this)); var pc = isa.pc(); if (pc != null) { diff --git a/vadl/main/vadl/viam/FloatExceptionFlag.java b/vadl/main/vadl/viam/FloatExceptionFlag.java new file mode 100644 index 000000000..87e463727 --- /dev/null +++ b/vadl/main/vadl/viam/FloatExceptionFlag.java @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.viam; + +import java.util.Arrays; +import java.util.Optional; + +/** + * Enum representing float exception flags. + * + *

    The supported flags are: + *

      + *
    • INVALID
    • + *
    • DIV_BY_ZERO
    • + *
    • OVERFLOW
    • + *
    • UNDERFLOW
    • + *
    • INEXACT
    • + *
    + */ +public enum FloatExceptionFlag { + INVALID("invalid", 0), + DIV_BY_ZERO("div_by_zero", 1), + OVERFLOW("overflow", 2), + UNDERFLOW("underflow", 3), + INEXACT("inexact", 4); + + public final String name; + + /** + * See softfloat-types.h + */ + public final int qemuFlagOffset; + + FloatExceptionFlag(String name, int qemuFlagOffset) { + this.name = name; + this.qemuFlagOffset = qemuFlagOffset; + } + + public static Optional from(String name) { + return Arrays.stream(values()).filter(f -> f.name.equals(name)).findFirst(); + } +} diff --git a/vadl/main/vadl/viam/FloatFormat.java b/vadl/main/vadl/viam/FloatFormat.java index 083e3e7c5..6be249956 100644 --- a/vadl/main/vadl/viam/FloatFormat.java +++ b/vadl/main/vadl/viam/FloatFormat.java @@ -16,6 +16,8 @@ package vadl.viam; +import static java.util.Objects.requireNonNull; + import javax.annotation.CheckForNull; import javax.annotation.Nullable; import vadl.types.Type; @@ -25,7 +27,35 @@ */ public class FloatFormat extends Definition implements DefProp.WithType { - private int size = 0; + /** + * Represents all supported float encodings. + */ + public enum Encoding { + IEEE32(32, true), + IEEE64(64, true); + + public final int size; + public final boolean ieee; + + Encoding(int size, boolean ieee) { + this.size = size; + this.ieee = ieee; + } + + /** + * Returns the IEEE encoding for the given bit-size. + */ + public static @Nullable Encoding ieee(int size) { + return switch (size) { + case 32 -> IEEE32; + case 64 -> IEEE64; + default -> null; + }; + } + } + + @Nullable + private Encoding encoding = null; @Nullable private Constant canonicalSNaN = null; @@ -36,8 +66,8 @@ public FloatFormat(Identifier identifier) { super(identifier); } - public void setSize(int size) { - this.size = size; + public void setEncoding(@CheckForNull Encoding encoding) { + this.encoding = encoding; } public void setCanonicalSNaN(@CheckForNull Constant canonicalSNaN) { @@ -49,10 +79,11 @@ public void setCanonicalQNaN(@CheckForNull Constant canonicalQNaN) { } /** - * The bit-size of the float format. + * The encoding of the float format. */ - public int size() { - return size; + @Nullable + public Encoding encoding() { + return encoding; } /** @@ -71,6 +102,13 @@ public Constant canonicalQNaN() { return canonicalQNaN; } + /** + * The name of the float format in lower case. + */ + public String nameLower() { + return simpleName().toLowerCase(); + } + @Override public Type type() { return Type.floatType(); @@ -81,6 +119,24 @@ public void accept(DefinitionVisitor visitor) { visitor.visit(this); } + @Override + public void verify() { + super.verify(); + // FIXME: for now this is checked here, but this should create a diagnostic instead of ViamError + ensure(encoding != null, "Encoding not specified"); + checkNaN(canonicalSNaN, "sNaN"); + checkNaN(canonicalQNaN, "qNaN"); + } + + private void checkNaN(@Nullable Constant value, String kind) { + ensure(value != null, "Canonical %s not specified", kind); + var valueBits = value.asVal().integer().bitLength(); + var givenBits = requireNonNull(encoding).size; + ensure(valueBits <= givenBits, + "Canonical %s value cannot require more bits (%d) than the encoding size (%d)", + kind, valueBits, givenBits); + } + @Override public String toString() { return identifier.simpleName(); diff --git a/vadl/main/vadl/viam/InstructionSetArchitecture.java b/vadl/main/vadl/viam/InstructionSetArchitecture.java index c9fa2130e..58deeb422 100644 --- a/vadl/main/vadl/viam/InstructionSetArchitecture.java +++ b/vadl/main/vadl/viam/InstructionSetArchitecture.java @@ -34,6 +34,7 @@ public class InstructionSetArchitecture extends Definition { @Nullable private final Counter pc; + private final List floatFormats; private final List formats; private final List functions; private final List operations; @@ -72,6 +73,7 @@ public InstructionSetArchitecture(Identifier identifier, @Nullable Counter pc, List memories, List artificialResources, + List floatFormats, @Nullable Group group ) { super(identifier); @@ -87,6 +89,7 @@ public InstructionSetArchitecture(Identifier identifier, this.pc = pc; this.memories = memories; this.artificialResources = artificialResources; + this.floatFormats = floatFormats; this.group = group; // set parent architecture of instructions @@ -171,6 +174,14 @@ public List registerTensors() { } + /** + * Returns the {@link FloatFormat}s owned by this ISA. + * So it might not include definitions accessible through the super ISA. + */ + public List ownFloatFormats() { + return floatFormats; + } + /** * Returns the {@link Format}s owned by this ISA. * So it might not include definitions accessible through the super ISA. diff --git a/vadl/main/vadl/viam/annotations/FloatFlagAnnotation.java b/vadl/main/vadl/viam/annotations/FloatFlagAnnotation.java new file mode 100644 index 000000000..2a3508332 --- /dev/null +++ b/vadl/main/vadl/viam/annotations/FloatFlagAnnotation.java @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.viam.annotations; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; +import vadl.viam.Annotation; +import vadl.viam.Constant; +import vadl.viam.FloatExceptionFlag; +import vadl.viam.RegisterTensor; + +/** + * Annotation for registers that are saved in the translation block state. Contains a + * {@link Constant.BitSlice} that specifies what bits of the register are saved. + */ +public class FloatFlagAnnotation extends Annotation { + + private final Map sticky = new HashMap<>(); + private final Map nonSticky = new HashMap<>(); + + @Override + public Class parentDefinitionClass() { + return RegisterTensor.class; + } + + public boolean isSticky(int index) { + return sticky.containsKey(index); + } + + @Nullable + public FloatExceptionFlag get(int index) { + return isSticky(index) ? sticky.get(index) : nonSticky.get(index); + } + + public void set(int index, boolean isSticky, FloatExceptionFlag flag) { + (isSticky ? sticky : nonSticky).put(index, flag); + } + + public Map stickyFlags() { + return sticky; + } + + public Map nonStickyFlags() { + return nonSticky; + } + +} diff --git a/vadl/main/vadl/viam/annotations/RegisterSliceAnnotation.java b/vadl/main/vadl/viam/annotations/RegisterSliceAnnotation.java new file mode 100644 index 000000000..eb4d7d5f8 --- /dev/null +++ b/vadl/main/vadl/viam/annotations/RegisterSliceAnnotation.java @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.viam.annotations; + +import java.util.stream.Stream; +import javax.annotation.Nullable; +import vadl.viam.Annotation; +import vadl.viam.Constant; +import vadl.viam.RegisterTensor; + +/** + * Annotation for registers, which contains a {@link Constant.BitSlice} that specifies + * what bits of the register are annotated. + */ +public abstract class RegisterSliceAnnotation extends Annotation { + + private final int registerBitWidth; + + /** + * Determines what bits of the register are annotated. + * If this is null, then the whole register (i.e. all bits) are annotated. + */ + @Nullable + private Constant.BitSlice slice; + + /** + * Constructs a new annotation for a register. If the given bit slice is `null`, then the + * whole register is annotated. Otherwise, only the bits in the slice are annotated. + */ + public RegisterSliceAnnotation(int registerBitWidth, @Nullable Constant.BitSlice slice) { + this.registerBitWidth = registerBitWidth; + this.slice = slice; + normalizeBitSlice(); + } + + @Override + public Class parentDefinitionClass() { + return RegisterTensor.class; + } + + /** + * Marks the bits in the given slice annotated, additional to the already marked bits. + * + * @param slice What bits to add + */ + public void addSlice(@Nullable Constant.BitSlice slice) { + if (slice == null || this.slice == null) { + this.slice = null; + } else { + new Constant.BitSlice(Stream.concat( + this.slice.parts(), slice.parts() + ).toArray(Constant.BitSlice.Part[]::new)); + normalizeBitSlice(); + } + } + + /** + * Returns whether the whole register is annotated. + */ + public boolean wholeRegister() { + return slice == null; + } + + /** + * Returns whether the given slice of bits of the register are annotated. + */ + public boolean covers(Constant.BitSlice slice) { + return this.slice == null || this.slice.covers(slice); + } + + @Nullable + public Constant.BitSlice slice() { + return slice; + } + + /** + * Returns how many bits are annotated. + */ + public int bitSize() { + if (slice == null) { + return registerBitWidth; + } + return slice.bitSize(); + } + + private void normalizeBitSlice() { + if (covers(Constant.BitSlice.of(registerBitWidth - 1, 0))) { + // set to null if the whole register is covered + slice = null; + } + if (slice != null) { + // merge all overlapping parts and ensure that every bit is only covered once + + // Note: calling slice.hasOverlappingParts() does NOT work here, because it does not + // check overlapping parts that are equal + slice = new Constant.BitSlice( + slice.stream().distinct() + .mapToObj(i -> new Constant.BitSlice.Part(i, i)) + .toArray(Constant.BitSlice.Part[]::new) + ); + } + } + +} diff --git a/vadl/main/vadl/viam/annotations/TbStateRegisterAnnotation.java b/vadl/main/vadl/viam/annotations/TbStateRegisterAnnotation.java index 188a48e85..dca2d9d89 100644 --- a/vadl/main/vadl/viam/annotations/TbStateRegisterAnnotation.java +++ b/vadl/main/vadl/viam/annotations/TbStateRegisterAnnotation.java @@ -16,27 +16,14 @@ package vadl.viam.annotations; -import java.util.stream.Stream; import javax.annotation.Nullable; -import vadl.viam.Annotation; import vadl.viam.Constant; -import vadl.viam.RegisterTensor; /** - * Annotation for registers or register aliases that are saved in the - * translation block state. Contains a {@link Constant.BitSlice} that specifies - * what bits of the register are saved. + * Annotation for registers that are saved in the translation block state. Contains a + * {@link Constant.BitSlice} that specifies what bits of the register are saved. */ -public class TbStateRegisterAnnotation extends Annotation { - - private final int registerBitWidth; - - /** - * Determines what bits of the register are saved in the translation block state. - * If this is null, then the whole register is saved. - */ - @Nullable - private Constant.BitSlice slice; +public class TbStateRegisterAnnotation extends RegisterSliceAnnotation { /** * Constructs a new annotation for a register (or register alias) that is saved @@ -45,79 +32,7 @@ public class TbStateRegisterAnnotation extends Annotation { * marked as saved. */ public TbStateRegisterAnnotation(int registerBitWidth, @Nullable Constant.BitSlice slice) { - this.registerBitWidth = registerBitWidth; - this.slice = slice; - normalizeBitSlice(); - } - - @Override - public Class parentDefinitionClass() { - return RegisterTensor.class; - } - - /** - * Marks the bits in the given slice as translation block state saved, additional - * to the already marked bits. - * - * @param slice What bits to add - */ - public void addSlice(@Nullable Constant.BitSlice slice) { - if (slice == null || this.slice == null) { - this.slice = null; - } else { - new Constant.BitSlice(Stream.concat( - this.slice.parts(), slice.parts() - ).toArray(Constant.BitSlice.Part[]::new)); - normalizeBitSlice(); - } - } - - /** - * Returns whether the whole register is saved in the translation block state. - */ - public boolean wholeRegister() { - return slice == null; - } - - /** - * Returns whether the given slice of bits of the register are saved in the - * translation block state. - */ - public boolean covers(Constant.BitSlice slice) { - return this.slice == null || this.slice.covers(slice); - } - - @Nullable - public Constant.BitSlice slice() { - return slice; - } - - /** - * Returns how many bits are marked as saved in the translation block state. - */ - public int bitSize() { - if (slice == null) { - return registerBitWidth; - } - return slice.bitSize(); - } - - private void normalizeBitSlice() { - if (covers(Constant.BitSlice.of(registerBitWidth - 1, 0))) { - // set to null if the whole register is covered - slice = null; - } - if (slice != null) { - // merge all overlapping parts and ensure that every bit is only covered once - - // Note: calling slice.hasOverlappingParts() does NOT work here, because it does not - // check overlapping parts that are equal - slice = new Constant.BitSlice( - slice.stream().distinct() - .mapToObj(i -> new Constant.BitSlice.Part(i, i)) - .toArray(Constant.BitSlice.Part[]::new) - ); - } + super(registerBitWidth, slice); } } diff --git a/vadl/test/vadl/iss/passes/IssTensorAssignmentToForallPassTest.java b/vadl/test/vadl/iss/passes/IssTensorAssignmentToForallPassTest.java index 925c1a31d..4cc898f56 100644 --- a/vadl/test/vadl/iss/passes/IssTensorAssignmentToForallPassTest.java +++ b/vadl/test/vadl/iss/passes/IssTensorAssignmentToForallPassTest.java @@ -176,6 +176,7 @@ private static Fixture createTensorWriteFixture(String name, null, Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), null )); diff --git a/vadl/test/vadl/viam/passes/constant_propagation/CanonicalizationPassTest.java b/vadl/test/vadl/viam/passes/constant_propagation/CanonicalizationPassTest.java index a30aa4b09..084e5cf6a 100644 --- a/vadl/test/vadl/viam/passes/constant_propagation/CanonicalizationPassTest.java +++ b/vadl/test/vadl/viam/passes/constant_propagation/CanonicalizationPassTest.java @@ -95,6 +95,7 @@ void shouldReplaceAdditionWithConstant() { null, Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), null ); From e04f10d240155859bc0e1ee9b50de067663deb77 Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Tue, 21 Jul 2026 09:54:41 +0200 Subject: [PATCH 04/11] wip: Fix float flag annotation Also optimizes generated qemu code size --- sys/risc-v/rv64f.vadl | 10 +- .../main/vadl/ast/AnnotationTable.java | 163 +++++++++--------- .../templates/iss/target/gen-arch/helper.c | 39 +++-- .../functionInterfaces/QuadConsumer.java | 51 ++++++ 4 files changed, 157 insertions(+), 106 deletions(-) create mode 100644 vadl/main/vadl/utils/functionInterfaces/QuadConsumer.java diff --git a/sys/risc-v/rv64f.vadl b/sys/risc-v/rv64f.vadl index c75a47e3a..4cd64eb04 100644 --- a/sys/risc-v/rv64f.vadl +++ b/sys/risc-v/rv64f.vadl @@ -29,11 +29,11 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { register F : Index -> FRegs - //[ sticky float flag : invalid, nv ] - //[ sticky float flag : div_by_zero, dz ] - //[ sticky float flag : overflow, of ] - //[ sticky float flag : underflow, uf ] - [ sticky float flag : inexact, nx ] + [ sticky fe flag invalid : nv ] + [ sticky fe flag div_by_zero : dz ] + [ sticky fe flag overflow : of ] + [ sticky fe flag underflow : uf ] + [ sticky fe flag inexact : nx ] register FCSR : FCsrFormat //alias register fcsr : FCsrFormat = CSR(CsrDefToImpl(CsrDef::fcsr)) diff --git a/vadl-frontend/main/vadl/ast/AnnotationTable.java b/vadl-frontend/main/vadl/ast/AnnotationTable.java index b2e42dddf..e0b6e2557 100644 --- a/vadl-frontend/main/vadl/ast/AnnotationTable.java +++ b/vadl-frontend/main/vadl/ast/AnnotationTable.java @@ -71,6 +71,7 @@ import vadl.types.BitsType; import vadl.types.Type; import vadl.utils.Pair; +import vadl.utils.functionInterfaces.QuadConsumer; import vadl.utils.functionInterfaces.TriConsumer; import vadl.viam.Abi; import vadl.viam.ArtificialResource; @@ -208,7 +209,7 @@ public class AnnotationTable { // this handled in the VIAM lowering when constructing the ArtificialResource .build(); - annotationOn(RegisterDefinition.class, "execution state", FormatFieldAnnotation::new) + annotationOn(RegisterDefinition.class, "execution state", ExecutionStateAnnotation::new) .check((def, annotation, lowering) -> annotation.typeCheckTarget(def)) .applyAst((def, annotation) -> annotation.calcBitSlice(def)) .applyViam((def, annotation, lowering) -> { @@ -356,36 +357,39 @@ public class AnnotationTable { .applyViam((def, annotation, lowering) -> ((FloatFormat) def).setCanonicalQNaN(annotation.constant.toViamConstant())).build(); - TriConsumer applyViamFloatFlag; - applyViamFloatFlag = (reg, annotation, sticky) -> { + QuadConsumer applyViamFloatFlag; + applyViamFloatFlag = (reg, annotation, sticky, flag) -> { var idx = annotation.index; if (reg.hasAnnotation(vadl.viam.annotations.FloatFlagAnnotation.class)) { var ann = reg.expectAnnotation(vadl.viam.annotations.FloatFlagAnnotation.class); - var flag = ann.get(idx); - ensure(flag == null, () -> error( + var setFlag = ann.get(idx); + ensure(setFlag == null, () -> error( "Bit already mapped as " + (ann.isSticky(idx) ? "" : "non ") - + "sticky " + requireNonNull(flag).name + " flag", + + "sticky " + requireNonNull(setFlag).name + " flag", annotation )); - ann.set(idx, sticky, annotation.flag); + ann.set(idx, sticky, flag); } else { var ann = new vadl.viam.annotations.FloatFlagAnnotation(); - ann.set(idx, sticky, annotation.flag); + ann.set(idx, sticky, flag); reg.addAnnotation(ann); } }; - annotationOn(RegisterDefinition.class, "float flag", FloatFlagAnnotation::new) - .check((def, annotation, lowering) -> annotation.typeCheckTarget(def)) - .applyViam((def, annotation, lowering) -> - applyViamFloatFlag.accept((RegisterTensor) def, annotation, false)) - .build(); + for (var flag : FloatExceptionFlag.values()) { + annotationOn(RegisterDefinition.class, "fe flag " + flag.name, FloatFlagAnnotation::new) + .check((def, annotation, lowering) -> annotation.typeCheckTarget(def)) + .applyViam((def, annotation, lowering) -> + applyViamFloatFlag.accept((RegisterTensor) def, annotation, false, flag)) + .build(); - annotationOn(RegisterDefinition.class, "sticky float flag", FloatFlagAnnotation::new) - .check((def, annotation, lowering) -> annotation.typeCheckTarget(def)) - .applyViam((def, annotation, lowering) -> - applyViamFloatFlag.accept((RegisterTensor) def, annotation, true)) - .build(); + annotationOn(RegisterDefinition.class, "sticky fe flag " + flag.name, + FloatFlagAnnotation::new) + .check((def, annotation, lowering) -> annotation.typeCheckTarget(def)) + .applyViam((def, annotation, lowering) -> + applyViamFloatFlag.accept((RegisterTensor) def, annotation, true, flag)) + .build(); + } /// PROCESSOR RELATED /// @@ -1160,10 +1164,7 @@ public String usageString() { } } -class FloatFlagAnnotation extends Annotation { - - @LazyInit - FloatExceptionFlag flag; +class FloatFlagAnnotation extends FormatFieldAnnotation { @LazyInit Identifier field; @@ -1171,56 +1172,72 @@ class FloatFlagAnnotation extends Annotation { @LazyInit int index; - @Override - void resolveName(AnnotationDefinition definition, SymbolTable.SymbolResolver resolver) { - } - @Override void typeCheck(AnnotationDefinition definition, TypeChecker typeChecker) { - verifyValuesCnt(definition, 2); - definition.values.forEach(def -> { - Diagnostic.ensure(def instanceof Identifier, () -> error("Invalid annotation value", def) - .description("An identifier was expected.") - ); - }); - var flagName = ((Identifier) definition.values.get(0)).name; - flag = FloatExceptionFlag.from(flagName).orElseThrow(() -> - error("Invalid float flag", definition) - .description("Given flag is %s, but must be one of %s", flagName, - Arrays.stream(FloatExceptionFlag.values()).map(f -> f.name) - .collect(Collectors.joining(", ")) - ).build() - ); - field = (Identifier) definition.values.get(1); + super.typeCheck(definition, typeChecker); + verifyValuesCnt(definition, 1); + field = (Identifier) definition.values.getFirst(); } void typeCheckTarget(TypedNode target) { - Diagnostic.ensure(target.type() instanceof FormatType, - () -> error("Annotation target has invalid type", this).description(""" - Float flag annotation can only be applied to \ - register definitions with a format type""")); - + super.typeCheckTarget(target); var format = ((FormatType) target.type()).format; - Function errBuilder = (String err) -> - error(err, field).description("Must be one of: %s", - format.fields.stream() - .filter(f -> !(f instanceof DerivedFormatField)) - .map(f -> f.identifier().name) - .collect(Collectors.joining(", ")) - ); - Diagnostic.ensure(format.hasField(field.name), - () -> errBuilder.apply("Unknown field name")); - Diagnostic.ensure(!(format.getField(field.name) instanceof DerivedFormatField), - () -> errBuilder.apply("Cannot annotate derived field")); var range = requireNonNull(format.getFieldRange(field.name)); Diagnostic.ensure(range.from() == range.to(), () -> error("Float flag can only be one bit", field)); index = range.from(); } + @Override + String annotationName() { + return "Float exception flag annotation"; + } + + @Override + public String usageString() { + return "[ " + name + " : ]"; + } +} + +/** + * An annotation that can be applied to registers of type {@link BitsType}. If the register's + * type is a {@link FormatType}, then this annotation can be used to reference its format fields. + * + *

    Usage examples: + *

    + * [ execution state ]
    + * register reg : Bits<8>
    + *
    + * [ execution state : f0, f1 ]
    + * register reg : Format
    + * format Format : Bits<8> { f0 [7], f1 [6], ... }
    + * 
    + */ +class ExecutionStateAnnotation extends FormatFieldAnnotation { + + void calcBitSlice(TypedNode target) { + if (fields.isEmpty()) { + var width = ((BitsType) target.type()).bitWidth(); + slice = Constant.BitSlice.of(width - 1, 0); + return; + } + var format = requireNonNull((FormatType) target.type()).format; + slice = new Constant.BitSlice( + fields.stream() + .map(field -> requireNonNull(format.getFieldRange(field.name))) + .map(range -> new Constant.BitSlice.Part(range.from(), range.to())) + .toArray(Constant.BitSlice.Part[]::new) + ); + } + + @Override + String annotationName() { + return "Execution state annotation"; + } + @Override public String usageString() { - return "[ " + name + " : , ]"; + return "[ " + name + " : , ... ]"; } } @@ -1239,7 +1256,7 @@ public String usageString() { * format Format : Bits<8> { f0 [7], f1 [6], ... } * */ -class FormatFieldAnnotation extends Annotation { +abstract class FormatFieldAnnotation extends Annotation { @LazyInit List fields; @@ -1266,14 +1283,14 @@ void typeCheckTarget(TypedNode target) { if (fields.isEmpty()) { Diagnostic.ensure(target.type() instanceof BitsType, () -> error("Annotation target has invalid type", this).description(""" - Execution state annotation can only be applied to simple \ - register definitions (no register files or tensors)""")); + %s can only be applied to simple register \ + definitions (no register files or tensors)""", annotationName())); return; } Diagnostic.ensure(target.type() instanceof FormatType, () -> error("Annotation target has invalid type", this).description(""" - Execution state annotation with format fields can only \ - be applied to register definitions with a format type""")); + %s with format fields can only be applied \ + to register definitions with a format type""", annotationName())); var format = ((FormatType) target.type()).format; fields.forEach(field -> { @@ -1291,25 +1308,7 @@ void typeCheckTarget(TypedNode target) { }); } - void calcBitSlice(TypedNode target) { - if (fields.isEmpty()) { - var width = ((BitsType) target.type()).bitWidth(); - slice = Constant.BitSlice.of(width - 1, 0); - return; - } - var format = requireNonNull((FormatType) target.type()).format; - slice = new Constant.BitSlice( - fields.stream() - .map(field -> requireNonNull(format.getFieldRange(field.name))) - .map(range -> new Constant.BitSlice.Part(range.from(), range.to())) - .toArray(Constant.BitSlice.Part[]::new) - ); - } - - @Override - public String usageString() { - return "[ " + name + " : , ... ]"; - } + abstract String annotationName(); } /** diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index 02f05cf16..67a3c3bea 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -37,7 +37,6 @@ void helper_unsupported(CPU[(${gen_arch_upper})]State *env) { // float helpers void prep_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status *s) { - // DEV NOTE: this is generated from float flag annotations uint16_t flags = 0xffff; // un-set non sticky flags [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.non_sticky_fe_flags}"] @@ -58,24 +57,26 @@ void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status env->[(${reg.name_lower})] &= ~((1 - (flags >> [(${flag.flag_idx})] & 1)) << [(${flag.idx})]);[/][/] } +#define FLOAT_FN_IEEE_FE_HELPER(S) \ + typedef uint##S##_t (*f##S##_fn)(uint##S##_t a, uint##S##_t b, float_status *s); \ + uint##S##_t f##S##_fn_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ + f##S##_fn fn, float_status *s, \ + uint##S##_t rs1, uint##S##_t rs2) { \ + prep_float_status_fe_flags(env, s); \ + uint##S##_t result = fn(rs1, rs2, s); \ + set_float_status_fe_flags(env, s); \ + return result; \ + } + +#define FLOAT_HELPER_2(S, NAME, FUN) \ + uint##S##_t helper_fadd_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2) { \ + return f##S##_fn_with_fe_flags(env, float##S##_##FUN, \ + &env->fp_status_##NAME, rs1, rs2); \ + } + [# th:each="size : ${float_ieee_sizes}"] -typedef uint[(${size})]_t (*f[(${size})]_fn)(uint[(${size})]_t a, uint[(${size})]_t b, float_status *s); -uint[(${size})]_t f[(${size})]_fn_with_fe_flags(CPU[(${gen_arch_upper})]State *env, f[(${size})]_fn fn, float_status *s, uint[(${size})]_t rs1, uint[(${size})]_t rs2) { - // DEV NOTE: flag stuff can be simplified when no flags present. But is that necessary? The flag functions - // should set all flags, if none are present in the spec. - // DEV NOTE: lets have a separate float_status for each float-type in the spec. but some things (e.g. rounding mode) - // are specified per call, so the float_status will be modified here either way (also because of flags). - // We could scratch that and rebuild a float_status from scratch every time -> env not affected. - prep_float_status_fe_flags(env, s); - uint[(${size})]_t result = fn(rs1, rs2, s); - set_float_status_fe_flags(env, s); - return result; -} -[/] +FLOAT_FN_IEEE_FE_HELPER([(${size})])[/] [# th:each="fmt : ${float_formats}"] -uint[(${fmt.bit_size})]_t helper_fadd_[(${fmt.name})](CPU[(${gen_arch_upper})]State *env, uint[(${fmt.bit_size})]_t rs1, uint[(${fmt.bit_size})]_t rs2) { - // TODO: float[(${fmt.bit_size})]_add can only be used for ieee formats. other formats will need other functions - return f[(${fmt.bit_size})]_fn_with_fe_flags(env, float[(${fmt.bit_size})]_add, &env->fp_status_[(${fmt.name})], rs1, rs2); -} -[/] +FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], add)[/] diff --git a/vadl/main/vadl/utils/functionInterfaces/QuadConsumer.java b/vadl/main/vadl/utils/functionInterfaces/QuadConsumer.java new file mode 100644 index 000000000..45c674e29 --- /dev/null +++ b/vadl/main/vadl/utils/functionInterfaces/QuadConsumer.java @@ -0,0 +1,51 @@ +package vadl.utils.functionInterfaces; + +import java.util.Objects; + +/** + * Represents an operation that accepts four input arguments and returns no result. + * This is the four-arity specialization of {@code Consumer}. + * Unlike most other functional interfaces, {@code QuadConsumer} is expected + * to operate via side effects. + * + * @param the type of the first argument to the operation + * @param the type of the second argument to the operation + * @param the type of the third argument to the operation + * @param the type of the fourth argument to the operation + * @see java.util.function.Consumer + * @see java.util.function.BiConsumer + */ +@FunctionalInterface +public interface QuadConsumer { + + /** + * Performs this operation on the given arguments. + * + * @param t the first input argument + * @param u the second input argument + * @param v the third input argument + * @param w the fourth input argument + */ + void accept(T t, U u, V v, W w); + + /** + * Returns a composed {@code QuadConsumer} that performs, in sequence, this + * operation followed by the {@code after} operation. If performing either + * operation throws an exception, it is relayed to the caller of the + * composed operation. If performing this operation throws an exception, + * the {@code after} operation will not be performed. + * + * @param after the operation to perform after this operation + * @return a composed {@code QuadConsumer} that performs in sequence this + * operation followed by the {@code after} operation + * @throws NullPointerException if {@code after} is null + */ + default QuadConsumer andThen( + QuadConsumer after) { + Objects.requireNonNull(after); + return (t, u, v, w) -> { + accept(t, u, v, w); + after.accept(t, u, v, w); + }; + } +} \ No newline at end of file From 451ad2f95c93bc99df2d23d63f19fa6f2a4f033e Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Thu, 23 Jul 2026 13:14:40 +0200 Subject: [PATCH 05/11] wip: Add risc-v float to/from int convert instructions --- sys/risc-v/rv64f.vadl | 66 +++++----- vadl-frontend/main/vadl/ast/TypeChecker.java | 2 +- .../passes/common/IssNormalizationPass.java | 40 ++++++ vadl/main/vadl/types/BuiltInTable.java | 120 +++++++++++++++++- vadl/main/vadl/types/Type.java | 3 +- .../VadlBuiltInEmptyNoStatusDispatcher.java | 32 +++++ .../utils/VadlBuiltInNoStatusDispatcher.java | 32 +++++ 7 files changed, 262 insertions(+), 33 deletions(-) diff --git a/sys/risc-v/rv64f.vadl b/sys/risc-v/rv64f.vadl index 4cd64eb04..09bed46cc 100644 --- a/sys/risc-v/rv64f.vadl +++ b/sys/risc-v/rv64f.vadl @@ -122,8 +122,6 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($c.rm))) } - record FSizeRec (suffix : Id, iSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, fTy : Id, size : Int) - [ canonical sNaN : 0x7fa00000 ] [ canonical qNaN : 0x7fc00000 ] [ IEEE : 32 ] @@ -134,6 +132,8 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { [ IEEE : 64 ] float-type IEEE64 + record FSizeRec (suffix : Id, iSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, fTy : Id, size : Int) + // TODO: IEEE16 and IEEE128 are not yet implemented model FSize16 () : FSizeRec = {(H ; H ; Fmt::h ; FP16 ; SIntH ; UIntH ; IEEE16 ; 16 )} model FSize32 () : FSizeRec = {(S ; W ; Fmt::s ; FP32 ; SIntW ; UIntW ; IEEE32 ; 32 )} @@ -151,16 +151,22 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { model NaNBox (size : FSizeRec, val : Ex) : Ex = { match : Ex ( - $size.size = 16 => $NaNBoxH($val); - $size.size = 32 => $NaNBoxS($val); - $size.size = 64 => $NaNBoxD($val); - _ => $NaNBoxQ($val) + $size.fTy = IEEE16 => $NaNBoxH($val); + $size.fTy = IEEE32 => $NaNBoxS($val); + $size.fTy = IEEE64 => $NaNBoxD($val); + _ => $NaNBoxQ($val) ) } - model-type BoolModel = (Ex, Ex) -> Ex - model Unsigned (t : Ex, f : Ex) : Ex = { t } - model Signed (t : Ex, f : Ex) : Ex = { t } + model-type BoolModelId = (Id, Id) -> Id + model-type BoolModelStr = (Str, Str) -> Str + model UnsignedId (u : Id, s : Id) : Id = { $u } + model SignedId (u : Id, s : Id) : Id = { $s } + model UnsignedStr (u : Str, s : Str) : Str = { $u } + model SignedStr (u : Str, s : Str) : Str = { $s } + record BoolModelRec (id : BoolModelId, str : BoolModelStr) + model Unsigned () : BoolModelRec = {(UnsignedId ; UnsignedStr)} + model Signed () : BoolModelRec = {(SignedId ; SignedStr )} model FRtypeInstrBiArith (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { $FRtypeInstr2rm (( @@ -230,25 +236,25 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { ) ; $rs2 ) } - model FRtypeInstrCvtX2F (name : Id, fSize : FSizeRec, iSize : FSizeRec, u : BoolModel, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { + model FRtypeInstrCvtX2F (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { $FRtypeInstr1rm (( - AsId($name, $fSize.suffix, $iSize.iSuffix, $u("U" ; "")) ; - AsStr($name, ".", $fSize.suffix, ".", $iSize.iSuffix, $u("U" ; "")) ; - $rm ; $fSize.fmt ; $funct5 ; - let result = $fun($fSize.fTy, X(rs1) as $u(iSize.uTy ; iSize.sTy), $rm) in { - F(rd) := $NaNBox($fSize ; result) + AsId($name, $size.suffix, $iSize.iSuffix, $u.str("U" ; "")) ; + AsStr($name, ".", $size.suffix, ".", $iSize.iSuffix, $u.str("U" ; "")) ; + $rm ; $size.fmt ; $funct5 ; + let result = $fun($size.fTy, X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), $rm) in { + F(rd) := $NaNBox($size ; result) } ) ; $rs2 ) } - model FRtypeInstrCvtF2X (name : Id, fSize : FSizeRec, iSize : FSizeRec, u : BoolModel, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { + model FRtypeInstrCvtF2X (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { $FRtypeInstr1rm (( - AsId($name, $iSize.iSuffix, $u("U" ; ""), $fSize.suffix) ; - AsStr($name, ".", $iSize.iSuffix, $u("U" ; ""), ".", $fSize.suffix) ; - $rm ; $fSize.fmt ; $funct5 ; - // TODO: we must make sure the builtin understands what type to convert to - let result = $fun($fSize.fTy, F(rs1) as $fSize.ty, $rm) in { - X(rd) := result as $u(iSize.uTy ; iSize.sTy) + AsId($name, $iSize.iSuffix, $u.str("U" ; ""), $size.suffix) ; + AsStr($name, ".", $iSize.iSuffix, $u.str("U" ; ""), ".", $size.suffix) ; + $rm ; $size.fmt ; $funct5 ; + let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { + // always sign extend the result, even unsigned results + X(rd) := result as SIntR } ) ; $rs2 ) } @@ -321,15 +327,15 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstrMvF2X (FMV ; $FSize32 ; 0b1'1100 ; 0b000 ; 0b0'0000) $FRtypeInstrMvX2F (FMV ; $FSize32 ; 0b1'1110 ; 0b000 ; 0b0'0000) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvt ; 0b1'1010 ; 0b0'0000 ; Frm::rne) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvt ; 0b1'1010 ; 0b0'0001 ; Frm::rne) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvt ; 0b1'1010 ; 0b0'0010 ; Frm::rne) - //$FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvt ; 0b1'1010 ; 0b0'0011 ; Frm::rne) + $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; (SignedId ; SignedStr) ; VADL::fcvtssf ; 0b1'1010 ; 0b0'0000 ; Frm::rne) + $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtusf ; 0b1'1010 ; 0b0'0001 ; Frm::rne) + $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; (SignedId ; SignedStr) ; VADL::fcvtsdf ; 0b1'1010 ; 0b0'0010 ; Frm::rne) + $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtudf ; 0b1'1010 ; 0b0'0011 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Signed ; VADL::fcvt ; 0b1'1000 ; 0b0'0000 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; Unsigned ; VADL::fcvt ; 0b1'1000 ; 0b0'0001 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Signed ; VADL::fcvt ; 0b1'1000 ; 0b0'0010 ; Frm::rne) - //$FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; Unsigned ; VADL::fcvt ; 0b1'1000 ; 0b0'0011 ; Frm::rne) + $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; (SignedId ; SignedStr) ; VADL::fcvtfss ; 0b1'1000 ; 0b0'0000 ; Frm::rne) + $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfus ; 0b1'1000 ; 0b0'0001 ; Frm::rne) + $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; (SignedId ; SignedStr) ; VADL::fcvtfsd ; 0b1'1000 ; 0b0'0010 ; Frm::rne) + $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfud ; 0b1'1000 ; 0b0'0011 ; Frm::rne) //$FR4typeInstr (FMADD ; $FSize32 ; VADL::fmadd ; Frm::rne) //$FR4typeInstr (FMSUB ; $FSize32 ; VADL::fmsub ; Frm::rne) diff --git a/vadl-frontend/main/vadl/ast/TypeChecker.java b/vadl-frontend/main/vadl/ast/TypeChecker.java index 759b4be1a..3cfb9fb62 100644 --- a/vadl-frontend/main/vadl/ast/TypeChecker.java +++ b/vadl-frontend/main/vadl/ast/TypeChecker.java @@ -995,7 +995,7 @@ private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, Li throw addErrorAndStopChecking( error("Type Mismatch", location) .locationDescription(location, - "Expected %d arguments but got %d.", builtIn.argTypeClasses().size(), args.size()) + "Expected %d arguments but got %d.", minArgCount, args.size()) .build()); } diff --git a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java index afaf1fca5..462a334b0 100644 --- a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java +++ b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java @@ -815,6 +815,46 @@ public void handleFADD(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } + @Override + public void handleFCVTFSS(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTFSD(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTFUS(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTFUD(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTSSF(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTSDF(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTUSF(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTUDF(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + @Override public void handleConcat(BuiltInCall input) { // do nothing (result is already fine) diff --git a/vadl/main/vadl/types/BuiltInTable.java b/vadl/main/vadl/types/BuiltInTable.java index d2ed90cc3..e697b1455 100644 --- a/vadl/main/vadl/types/BuiltInTable.java +++ b/vadl/main/vadl/types/BuiltInTable.java @@ -17,7 +17,9 @@ package vadl.types; import static org.slf4j.LoggerFactory.getLogger; +import static vadl.types.Type.bits; import static vadl.types.Type.constructDataType; +import static vadl.types.Type.signedInt; import com.google.errorprone.annotations.FormatMethod; import java.math.BigInteger; @@ -1067,6 +1069,103 @@ public class BuiltInTable { .returnsFirstBitWidthAndFloatStatus() .build(); + ///// FLOAT TO INT CONVERSION ////// + + /** + * Float conversion from float to signed single. + * {@code function fcvtfss( t : FloatType, a : Bits, rm : Bits<3> ) -> SInt<32> } + */ + public static final BuiltIn FCVTFSS = + func("VADL::fcvtfss", + Type.relation(List.of(BitsType.class, BitsType.class), 1, SIntType.class)) + .takesFrm(1) + .returns(signedInt(32)) + .build(); + + /** + * Float conversion from float to signed double. + * {@code function fcvtfsd( t : FloatType, a : Bits, rm : Bits<3> ) -> SInt<64> } + */ + public static final BuiltIn FCVTFSD = + func("VADL::fcvtfsd", + Type.relation(List.of(BitsType.class, BitsType.class), 1, SIntType.class)) + .takesFrm(1) + .returns(signedInt(64)) + .build(); + + /** + * Float conversion from float to unsigned single. + * {@code function fcvtfus( t : FloatType, a : Bits, rm : Bits<3> ) -> UInt<32> } + */ + public static final BuiltIn FCVTFUS = + func("VADL::fcvtfus", + Type.relation(List.of(BitsType.class, BitsType.class), 1, UIntType.class)) + .takesFrm(1) + .returns(signedInt(32)) + .build(); + + /** + * Float conversion from float to unsigned double. + * {@code function fcvtfud( t : FloatType, a : Bits, rm : Bits<3> ) -> UInt<64> } + */ + public static final BuiltIn FCVTFUD = + func("VADL::fcvtfud", + Type.relation(List.of(BitsType.class, BitsType.class), 1, UIntType.class)) + .takesFrm(1) + .returns(signedInt(64)) + .build(); + + ///// INT TO FLOAT CONVERSION ////// + + // FIXME: here there is a problem: how does the type system infer how large the returned + // float type is? For now, its hardcoded at 32 bits, but it should actually be determined + // by the passed float-type. Idea: can the type-checker handle this special case? + // We could use type parameters... + + /** + * Float conversion from signed single to float. + * {@code function fcvtssf( t : FloatType, a : SInt<32>, rm : Bits<3> ) -> Bits<32> } + */ + public static final BuiltIn FCVTSSF = + func("VADL::fcvtssf", + Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(32)) + .build(); + + /** + * Float conversion from signed double to float. + * {@code function fcvtsdf( t : FloatType, a : SInt<64>, rm : Bits<3> ) -> Bits<32> } + */ + public static final BuiltIn FCVTSDF = + func("VADL::fcvtsdf", + Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(32)) + .build(); + + /** + * Float conversion from unsigned single to float. + * {@code function fcvtusf( t : FloatType, a : UInt<32>, rm : Bits<3> ) -> Bits<32> } + */ + public static final BuiltIn FCVTUSF = + func("VADL::fcvtusf", + Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(32)) + .build(); + + /** + * Float conversion from unsigned double to float. + * {@code function fcvtudf( t : FloatType, a : UInt<64>, rm : Bits<3> ) -> Bits<32> } + */ + public static final BuiltIn FCVTUDF = + func("VADL::fcvtudf", + Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(32)) + .build(); + ///// FUNCTIONS ////// @@ -1442,6 +1541,17 @@ private static BuiltIn instr(String name) { FADD ); + public static final List FLOAT_CONVERSION_BUILT_INS = List.of( + FCVTFSS, + FCVTFSD, + FCVTFUS, + FCVTFUD, + FCVTSSF, + FCVTSDF, + FCVTUSF, + FCVTUDF + ); + public static final List FUNCTION_BUILT_INS = List.of( MNEMONIC, CONCATENATE_STRINGS, @@ -1473,7 +1583,8 @@ private static BuiltIn instr(String name) { ); public static final List FLOAT_BUILT_INS = Stream.of( - FLOAT_ARITHMETIC_BUILT_INS.stream() + FLOAT_ARITHMETIC_BUILT_INS.stream(), + FLOAT_CONVERSION_BUILT_INS.stream() ).flatMap(s -> s).toList(); public static final List BUILT_INS = Stream.of( @@ -1864,6 +1975,13 @@ public BuiltInBuilder takesFirstTwoWithSameBitWidths() { return this; } + public BuiltInBuilder takesFrm(int roundingModeArgIdx) { + takesData((args) -> args.size() > roundingModeArgIdx + && args.get(roundingModeArgIdx).bitWidth() == 3 + ); + return this; + } + public BuiltInBuilder takesFirstTwoWithSameBitWidthsAndFrm(int roundingModeArgIdx) { takesData((args) -> args.size() > roundingModeArgIdx && args.get(0).bitWidth() == args.get(1).bitWidth() diff --git a/vadl/main/vadl/types/Type.java b/vadl/main/vadl/types/Type.java index 1e727e7eb..2c26784e3 100644 --- a/vadl/main/vadl/types/Type.java +++ b/vadl/main/vadl/types/Type.java @@ -307,6 +307,7 @@ public static RelationType relation(List> argTypes, * * @param argTypes the list of argument type classes * @param hasVarArgs the flag indicating if the last argument of kind varargs + * @param floatTypeArgCount the amount of float-type arguments preceding other args * @param returnType the return type class * @return the RelationType instance */ @@ -314,7 +315,7 @@ public static RelationType relation(List> argTypes, boolean hasVarArgs, int floatTypeArgCount, Class returnType) { - var hashCode = Objects.hash(argTypes, hasVarArgs, returnType); + var hashCode = Objects.hash(argTypes, hasVarArgs, floatTypeArgCount, returnType); return relationTypes.computeIfAbsent(hashCode, k -> new RelationType(argTypes, hasVarArgs, floatTypeArgCount, returnType)); } diff --git a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java index ea59f067c..1cba631c1 100644 --- a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java @@ -194,6 +194,38 @@ default void handleCTO(T input) { default void handleFADD(T input) { } + @Override + default void handleFCVTFSS(T input) { + } + + @Override + default void handleFCVTFSD(T input) { + } + + @Override + default void handleFCVTFUS(T input) { + } + + @Override + default void handleFCVTFUD(T input) { + } + + @Override + default void handleFCVTSSF(T input) { + } + + @Override + default void handleFCVTSDF(T input) { + } + + @Override + default void handleFCVTUSF(T input) { + } + + @Override + default void handleFCVTUDF(T input) { + } + @Override default void handleConcat(T input) { } diff --git a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java index d84c71549..c0a78e105 100644 --- a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java @@ -115,6 +115,22 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { handleCTO(input); } else if (builtIn == BuiltInTable.FADD) { handleFADD(input); + } else if (builtIn == BuiltInTable.FCVTFSS) { + handleFCVTFSS(input); + } else if (builtIn == BuiltInTable.FCVTFSD) { + handleFCVTFSD(input); + } else if (builtIn == BuiltInTable.FCVTFUS) { + handleFCVTFUS(input); + } else if (builtIn == BuiltInTable.FCVTFUD) { + handleFCVTFUD(input); + } else if (builtIn == BuiltInTable.FCVTSSF) { + handleFCVTSSF(input); + } else if (builtIn == BuiltInTable.FCVTSDF) { + handleFCVTSDF(input); + } else if (builtIn == BuiltInTable.FCVTUSF) { + handleFCVTUSF(input); + } else if (builtIn == BuiltInTable.FCVTUDF) { + handleFCVTUDF(input); } else if (builtIn == BuiltInTable.CONCATENATE_BITS) { handleConcat(input); } else { @@ -209,6 +225,22 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { void handleFADD(T input); + void handleFCVTFSS(T input); + + void handleFCVTFSD(T input); + + void handleFCVTFUS(T input); + + void handleFCVTFUD(T input); + + void handleFCVTSSF(T input); + + void handleFCVTSDF(T input); + + void handleFCVTUSF(T input); + + void handleFCVTUDF(T input); + void handleConcat(T input); } From c175a261fdb3d019377c940f750e4ff82b12153a Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Sat, 25 Jul 2026 00:19:58 +0200 Subject: [PATCH 06/11] wip: Add remaining risc-v float built-ins + qemu impl --- sys/risc-v/rv64f.vadl | 54 ++-- .../main/vadl/ast/AnnotationTable.java | 4 +- .../templates/iss/target/gen-arch/helper.c | 131 ++++++++-- .../templates/iss/target/gen-arch/helper.h | 33 ++- .../passes/common/IssNormalizationPass.java | 95 +++++++ .../tcg/lowering/TcgOpLoweringPass.java | 59 ++++- vadl/main/vadl/types/BuiltInTable.java | 243 +++++++++++++++++- .../VadlBuiltInEmptyNoStatusDispatcher.java | 78 ++++++ .../utils/VadlBuiltInNoStatusDispatcher.java | 76 ++++++ 9 files changed, 720 insertions(+), 53 deletions(-) diff --git a/sys/risc-v/rv64f.vadl b/sys/risc-v/rv64f.vadl index 09bed46cc..2941d627a 100644 --- a/sys/risc-v/rv64f.vadl +++ b/sys/risc-v/rv64f.vadl @@ -259,20 +259,20 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { ) ; $rs2 ) } - model FRtypeInstrClass (name : Id, suffix : Id, ty : Id) : IsaDefs = { - instruction AsId($name, $suffix) : Rtype = - let f = F(rs1) as ty in + model FRtypeInstrClass (name : Id, size : FSizeRec) : IsaDefs = { + instruction AsId($name, $size.suffix) : Rtype = + let f = F(rs1) as $size.ty in // TODO: this is from QEMU's risc-v vector_helper.c fclass_s(...) // what predicates should we implement? - let neg = VADL::fisneg(f) in - X(rd) := if VADL::fisinf (f) then ( if neg then 1 << 0 else 1 << 7 ) else - if VADL::fiszero (f) then ( if neg then 1 << 3 else 1 << 4 ) else - if VADL::fisdenorm(f) then ( if neg then 1 << 2 else 1 << 5 ) else - if VADL::fissnan (f) then 1 << 8 else - if VADL::fisqnan (f) then 1 << 9 else + let neg = VADL::fisneg($size.fTy, f) in + X(rd) := if VADL::fisinf ($size.fTy, f) then ( if neg then 1 << 0 else 1 << 7 ) else + if VADL::fiszero ($size.fTy, f) then ( if neg then 1 << 3 else 1 << 4 ) else + if VADL::fisdenorm($size.fTy, f) then ( if neg then 1 << 2 else 1 << 5 ) else + if VADL::fissnan ($size.fTy, f) then 1 << 8 else + if VADL::fisqnan ($size.fTy, f) then 1 << 9 else ( if neg then 1 << 1 else 1 << 6 ) - encoding AsId($name, $suffix) = {opcode = 0b101'0011, funct3 = 0b001, rs2 = 0b0'0000, funct7 = 0b111'0000} - assembly AsId($name, $suffix) = (AsStr($name), ".", AsStr($suffix), " ", register(rd), ",", register(rs1)) + encoding AsId($name, $size.suffix) = {opcode = 0b101'0011, funct3 = 0b001, rs2 = 0b0'0000, funct7 = 0b111'0000} + assembly AsId($name, $size.suffix) = (AsStr($name), ".", AsStr($size.suffix), " ", register(rd), ",", register(rs1)) } model FLtypeInstr (name : Id, size : FSizeRec, funct3 : Bin) : IsaDefs = { @@ -293,12 +293,12 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { assembly $name = (mnemonic, " ", register(rs2), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") } - model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex, opcode : Bin) : IsaDefs = { instruction AsId($name, $size.suffix) : R4type = let result = $fun($size.fTy, F(rs1), F(rs2), F(rs3), $rm) in { F(rd) := $NaNBox($size ; result) } - encoding AsId($name, $size.suffix) = {opcode = 0b100'0011, funct3 = $rm, fmt = $size.fmt} + encoding AsId($name, $size.suffix) = {opcode = $opcode, funct3 = $rm, fmt = $size.fmt} assembly AsId($name, $size.suffix) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($rm)) } @@ -306,19 +306,19 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FStypeInstr (FSW ; $FSize32 ; 0b010) $FRtypeInstrBiArith (FADD ; $FSize32 ; VADL::fadd ; 0b0'0000 ; Frm::rne) - //$FRtypeInstrBiArith (FSUB ; $FSize32 ; VADL::fsub ; 0b0'0001 ; Frm::rne) - //$FRtypeInstrBiArith (FMUL ; $FSize32 ; VADL::fmul ; 0b0'0010 ; Frm::rne) - //$FRtypeInstrBiArith (FDIV ; $FSize32 ; VADL::fdiv ; 0b0'0011 ; Frm::rne) + $FRtypeInstrBiArith (FSUB ; $FSize32 ; VADL::fsub ; 0b0'0001 ; Frm::rne) + $FRtypeInstrBiArith (FMUL ; $FSize32 ; VADL::fmul ; 0b0'0010 ; Frm::rne) + $FRtypeInstrBiArith (FDIV ; $FSize32 ; VADL::fdiv ; 0b0'0011 ; Frm::rne) - //$FRtypeInstrMinMax (FMIN ; $FSize32 ; VADL::fmin ; 0b0'0101 ; 0b000) - //$FRtypeInstrMinMax (FMAX ; $FSize32 ; VADL::fmax ; 0b0'0101 ; 0b001) + $FRtypeInstrMinMax (FMIN ; $FSize32 ; VADL::fmin ; 0b0'0101 ; 0b000) + $FRtypeInstrMinMax (FMAX ; $FSize32 ; VADL::fmax ; 0b0'0101 ; 0b001) - //$FRtypeInstrSqrt (FSQRT ; $FSize32 ; VADL::fsqrt ; 0b0'1011 ; 0b0'0000 ; Frm::rne) + $FRtypeInstrSqrt (FSQRT ; $FSize32 ; VADL::fsqrt ; 0b0'1011 ; 0b0'0000 ; Frm::rne) //// TODO: specify somehow that lt, le are signaling and eq is a quiet comparison - //$FRtypeInstrCmp (FLE ; $FSize32 ; VADL::fle ; 0b1'0100 ; 0b000) - //$FRtypeInstrCmp (FLT ; $FSize32 ; VADL::flt ; 0b1'0100 ; 0b001) - //$FRtypeInstrCmp (FEQ ; $FSize32 ; VADL::feq ; 0b1'0100 ; 0b010) + $FRtypeInstrCmp (FLE ; $FSize32 ; VADL::fle ; 0b1'0100 ; 0b000) + $FRtypeInstrCmp (FLT ; $FSize32 ; VADL::flt ; 0b1'0100 ; 0b001) + $FRtypeInstrCmp (FEQ ; $FSize32 ; VADL::feq ; 0b1'0100 ; 0b010) $FRtypeInstrSgn (FSGNJ ; $FSize32 ; ( rs2Sgn) ; 0b0'0100 ; 0b000) $FRtypeInstrSgn (FSGNJN ; $FSize32 ; ( 1 - rs2Sgn) ; 0b0'0100 ; 0b001) @@ -337,12 +337,12 @@ instruction set architecture RV64IMF extending RV64IMZicsr = { $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; (SignedId ; SignedStr) ; VADL::fcvtfsd ; 0b1'1000 ; 0b0'0010 ; Frm::rne) $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfud ; 0b1'1000 ; 0b0'0011 ; Frm::rne) - //$FR4typeInstr (FMADD ; $FSize32 ; VADL::fmadd ; Frm::rne) - //$FR4typeInstr (FMSUB ; $FSize32 ; VADL::fmsub ; Frm::rne) - //$FR4typeInstr (FNMADD ; $FSize32 ; VADL::fnmadd ; Frm::rne) - //$FR4typeInstr (FNMSUB ; $FSize32 ; VADL::fnmsub ; Frm::rne) + $FR4typeInstr (FMADD ; $FSize32 ; VADL::fmadd ; Frm::rne ; 0b100'0011) + $FR4typeInstr (FMSUB ; $FSize32 ; VADL::fmsub ; Frm::rne ; 0b100'0111) + $FR4typeInstr (FNMADD ; $FSize32 ; VADL::fnmadd ; Frm::rne ; 0b100'1111) + $FR4typeInstr (FNMSUB ; $FSize32 ; VADL::fnmsub ; Frm::rne ; 0b100'1011) - //$FRtypeInstrClass (FCLASS ; S ; FP32) + $FRtypeInstrClass (FCLASS ; $FSize32) } diff --git a/vadl-frontend/main/vadl/ast/AnnotationTable.java b/vadl-frontend/main/vadl/ast/AnnotationTable.java index e0b6e2557..9116b6dac 100644 --- a/vadl-frontend/main/vadl/ast/AnnotationTable.java +++ b/vadl-frontend/main/vadl/ast/AnnotationTable.java @@ -357,7 +357,8 @@ public class AnnotationTable { .applyViam((def, annotation, lowering) -> ((FloatFormat) def).setCanonicalQNaN(annotation.constant.toViamConstant())).build(); - QuadConsumer applyViamFloatFlag; + QuadConsumer + applyViamFloatFlag; applyViamFloatFlag = (reg, annotation, sticky, flag) -> { var idx = annotation.index; if (reg.hasAnnotation(vadl.viam.annotations.FloatFlagAnnotation.class)) { @@ -1179,6 +1180,7 @@ void typeCheck(AnnotationDefinition definition, TypeChecker typeChecker) { field = (Identifier) definition.values.getFirst(); } + @Override void typeCheckTarget(TypedNode target) { super.typeCheckTarget(target); var format = ((FormatType) target.type()).format; diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index 67a3c3bea..e062bff44 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -58,25 +58,124 @@ void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status } #define FLOAT_FN_IEEE_FE_HELPER(S) \ - typedef uint##S##_t (*f##S##_fn)(uint##S##_t a, uint##S##_t b, float_status *s); \ - uint##S##_t f##S##_fn_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ - f##S##_fn fn, float_status *s, \ - uint##S##_t rs1, uint##S##_t rs2) { \ - prep_float_status_fe_flags(env, s); \ - uint##S##_t result = fn(rs1, rs2, s); \ - set_float_status_fe_flags(env, s); \ - return result; \ + typedef uint##S##_t (*f##S##_fn_1)(uint##S##_t rs1, float_status *s); \ + typedef uint##S##_t (*f##S##_fn_2)(uint##S##_t rs1, uint##S##_t rs2, float_status *s); \ + typedef uint##S##_t (*f##S##_fn_3)(uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3, \ + int flags, float_status *s); \ + uint##S##_t f##S##_fn_1_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ + f##S##_fn_1 fn, float_status *s, \ + uint##S##_t rs1) { \ + prep_float_status_fe_flags(env, s); \ + uint##S##_t result = fn(rs1, s); \ + set_float_status_fe_flags(env, s); \ + return result; \ + } \ + uint##S##_t f##S##_fn_2_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ + f##S##_fn_2 fn, float_status *s, \ + uint##S##_t rs1, uint##S##_t rs2) { \ + prep_float_status_fe_flags(env, s); \ + uint##S##_t result = fn(rs1, rs2, s); \ + set_float_status_fe_flags(env, s); \ + return result; \ + } \ + uint##S##_t f##S##_fn_3_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ + f##S##_fn_3 fn, float_status *s, int flags, \ + uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3) { \ + prep_float_status_fe_flags(env, s); \ + uint##S##_t result = fn(rs1, rs2, rs3, flags, s); \ + set_float_status_fe_flags(env, s); \ + return result; \ } -#define FLOAT_HELPER_2(S, NAME, FUN) \ - uint##S##_t helper_fadd_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1, uint##S##_t rs2) { \ - return f##S##_fn_with_fe_flags(env, float##S##_##FUN, \ - &env->fp_status_##NAME, rs1, rs2); \ +#define FLOAT_HELPER_1(S, FMT, NAME, QEMU_FUN) \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + return f64_fn_1_with_fe_flags(env, float##S##_##QEMU_FUN, \ + &env->fp_status_##FMT, rs1); \ } -[# th:each="size : ${float_ieee_sizes}"] -FLOAT_FN_IEEE_FE_HELPER([(${size})])[/] +#define FLOAT_HELPER_2(S, FMT, NAME, QEMU_FUN) \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2) { \ + return f64_fn_2_with_fe_flags(env, float##S##_##QEMU_FUN, \ + &env->fp_status_##FMT, rs1, rs2); \ + } + +#define FLOAT_HELPER_3(S, FMT, NAME, QEMU_FUN, FLAGS) \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3) { \ + return f64_fn_3_with_fe_flags(env, float##S##_##QEMU_FUN, FLAGS, \ + &env->fp_status_##FMT, rs1, rs2, rs3); \ + } + +#define FLOAT_HELPER_F2I(S, FMT, INT_FMT, NAME) \ + INT_FMT##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + return f64_fn_1_with_fe_flags(env, float##S##_to_##INT_FMT, \ + &env->fp_status_##FMT, rs1); \ + } + +#define FLOAT_HELPER_I2F(S, FMT, INT_FMT, NAME) \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + INT_FMT##_t rs1) { \ + return f64_fn_1_with_fe_flags(env, INT_FMT##_to_##float##S, \ + &env->fp_status_##FMT, rs1); \ + } + +// TODO: optimize fe flags (maybe prep can be omitted; or flags set to avoid recomputation) +#define FLOAT_HELPER_CMP(S, FMT, NAME, QEMU_FUN) \ + uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2) { \ + return f64_fn_2_with_fe_flags(env, float##S##_##QEMU_FUN, \ + &env->fp_status_##FMT, rs1, rs2); \ + } + +#define FLOAT_HELPER_CLASS(S, FMT, NAME, QEMU_FUN) \ + uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + return f64_fn_1_with_fe_flags(env, float##S##_##QEMU_FUN, \ + &env->fp_status_##FMT, rs1); \ + } + +// just use uint64_t for everything for now to keep things simple +// the actual helper call signatures do contain the right sizes (and all unsigned) +FLOAT_FN_IEEE_FE_HELPER(64) [# th:each="fmt : ${float_formats}"] -FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], add)[/] +FLOAT_HELPER_1([(${fmt.bit_size})], [(${fmt.name})], fsqrt, sqrt) + +FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fadd, add) +FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fsub, sub) +FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fmul, mul) +FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fdiv, div) + +FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fmadd, muladd, 0) +FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fmsub, muladd, float_muladd_negate_c) +FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fnmadd, muladd, float_muladd_negate_c | float_muladd_negate_product) +FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fnmsub, muladd, float_muladd_negate_product) + +FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fmin, minimum_number) +FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fmax, maximum_number) + +// TODO: risc-v specifies eq as quiet. other ISAs might want to configure this +FLOAT_HELPER_CMP([(${fmt.bit_size})], [(${fmt.name})], flt, lt) +FLOAT_HELPER_CMP([(${fmt.bit_size})], [(${fmt.name})], fle, le) +FLOAT_HELPER_CMP([(${fmt.bit_size})], [(${fmt.name})], feq, eq_quiet) + +FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtfss) +FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtfsd) +FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtfus) +FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtfud) + +FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtssf) +FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtsdf) +FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtusf) +FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtudf) + +FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisinf, is_infinity) +FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fiszero, is_zero) +FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisneg, is_neg) +FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisdenorm, is_denormal) // TODO: less efficient than is_zero_or_denormal +FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fissnan, is_signaling_nan) +FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisqnan, is_quiet_nan) +[/] diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.h b/vadl/main/resources/templates/iss/target/gen-arch/helper.h index 04ceac7fa..e227e32fd 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.h @@ -14,4 +14,35 @@ DEF_HELPER_1(unsupported, noreturn, env) // float helpers [# th:each="fmt : ${float_formats}"] -DEF_HELPER_FLAGS_3(fadd_[(${fmt.name})], 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})])[/] +DEF_HELPER_FLAGS_2([(${fmt.name})]_fsqrt, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_fadd, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_fsub, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_fmul, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_fdiv, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_4([(${fmt.name})]_fmadd, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_4([(${fmt.name})]_fmsub, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_4([(${fmt.name})]_fnmadd, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_4([(${fmt.name})]_fnmsub, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_fmin, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_fmax, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) + +DEF_HELPER_FLAGS_3([(${fmt.name})]_flt, 0, i64, env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_fle, 0, i64, env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_3([(${fmt.name})]_feq, 0, i64, env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) + +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfss, 0, i32, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfsd, 0, i64, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfus, 0, i32, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfud, 0, i64, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtssf, 0, i[(${fmt.bit_size})], env, i32) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtsdf, 0, i[(${fmt.bit_size})], env, i64) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtusf, 0, i[(${fmt.bit_size})], env, i32) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtudf, 0, i[(${fmt.bit_size})], env, i64) + +DEF_HELPER_FLAGS_2([(${fmt.name})]_fisinf, 0, i64, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fiszero, 0, i64, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fisneg, 0, i64, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fisdenorm, 0, i64, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fissnan, 0, i64, env, i[(${fmt.bit_size})]) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fisqnan, 0, i64, env, i[(${fmt.bit_size})]) +[/] \ No newline at end of file diff --git a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java index 462a334b0..225ea6caf 100644 --- a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java +++ b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java @@ -810,11 +810,76 @@ public void handleCTO(BuiltInCall input) { throw graphError(input, "Normalization not yet implemented for this built-in"); } + @Override + public void handleFSQRT(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + @Override public void handleFADD(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } + @Override + public void handleFSUB(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFMUL(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFDIV(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFMADD(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFMSUB(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFNMADD(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFNMSUB(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFMIN(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFMAX(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFLT(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFLE(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFEQ(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + @Override public void handleFCVTFSS(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) @@ -855,6 +920,36 @@ public void handleFCVTUDF(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } + @Override + public void handleFISINF(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFISZERO(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFISNEG(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFISDENORM(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFISSNAN(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFISQNAN(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + @Override public void handleConcat(BuiltInCall input) { // do nothing (result is already fine) diff --git a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java index 939fa2768..5a5d1570a 100644 --- a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java +++ b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.IntStream; import javax.annotation.Nullable; import vadl.configuration.IssConfiguration; import vadl.iss.passes.AbstractIssPass; @@ -1258,12 +1259,43 @@ class BuiltInTcgLoweringExecutor { //// Float Arithmetic //// - .set(BuiltInTable.FADD, (ctx) -> out( - new TcgHelperCall( - ctx.dest(), new NodeList<>(ctx.src(0), ctx.src(1)), true, - "fadd_" + ctx.floatFormat(0).nameLower() - ) - )) + .set(BuiltInTable.FSQRT, (ctx) -> floatHelperCall(ctx, 1, "fsqrt")) + .set(BuiltInTable.FADD, (ctx) -> floatHelperCall(ctx, 2, "fadd")) + .set(BuiltInTable.FSUB, (ctx) -> floatHelperCall(ctx, 2, "fsub")) + .set(BuiltInTable.FMUL, (ctx) -> floatHelperCall(ctx, 2, "fmul")) + .set(BuiltInTable.FDIV, (ctx) -> floatHelperCall(ctx, 2, "fdiv")) + .set(BuiltInTable.FMADD, (ctx) -> floatHelperCall(ctx, 3, "fmadd")) + .set(BuiltInTable.FMSUB, (ctx) -> floatHelperCall(ctx, 3, "fmsub")) + .set(BuiltInTable.FNMADD, (ctx) -> floatHelperCall(ctx, 3, "fnmadd")) + .set(BuiltInTable.FNMSUB, (ctx) -> floatHelperCall(ctx, 3, "fnmsub")) + .set(BuiltInTable.FMIN, (ctx) -> floatHelperCall(ctx, 2, "fmin")) + .set(BuiltInTable.FMAX, (ctx) -> floatHelperCall(ctx, 2, "fmax")) + + //// Float Comparison //// + + .set(BuiltInTable.FLT, (ctx) -> floatHelperCall(ctx, 2, "flt")) + .set(BuiltInTable.FLE, (ctx) -> floatHelperCall(ctx, 2, "fle")) + .set(BuiltInTable.FEQ, (ctx) -> floatHelperCall(ctx, 2, "feq")) + + //// Float to Int Conversion //// + + .set(BuiltInTable.FCVTFSS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfss")) + .set(BuiltInTable.FCVTFSD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfsd")) + .set(BuiltInTable.FCVTFUS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfus")) + .set(BuiltInTable.FCVTFUD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfud")) + .set(BuiltInTable.FCVTSSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtssf")) + .set(BuiltInTable.FCVTSDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtsdf")) + .set(BuiltInTable.FCVTUSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtusf")) + .set(BuiltInTable.FCVTUDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtudf")) + + //// Float Classification //// + + .set(BuiltInTable.FISINF, (ctx) -> floatHelperCall(ctx, 1, "fisinf")) + .set(BuiltInTable.FISZERO, (ctx) -> floatHelperCall(ctx, 1, "fiszero")) + .set(BuiltInTable.FISNEG, (ctx) -> floatHelperCall(ctx, 1, "fisneg")) + .set(BuiltInTable.FISDENORM, (ctx) -> floatHelperCall(ctx, 1, "fisdenorm")) + .set(BuiltInTable.FISSNAN, (ctx) -> floatHelperCall(ctx, 1, "fissnan")) + .set(BuiltInTable.FISQNAN, (ctx) -> floatHelperCall(ctx, 1, "fisqnan")) .build(); } @@ -1297,6 +1329,21 @@ private static BuiltInResult out(TcgNode... nodes) { return new BuiltInResult(List.of(nodes)); } + /** + * Helper method to create a {@link BuiltInResult} from a helper call for a float built-in. + * + * @param ctx The built-in lowering context. + * @param argc The number of arguments the float built-in takes. + * @return A {@link BuiltInResult} containing the helper call. + */ + private static BuiltInResult floatHelperCall(BuiltInTcgLoweringExecutor.Context ctx, int argc, + String name) { + return out(new TcgHelperCall( + ctx.dest(), new NodeList<>(IntStream.range(0, argc).mapToObj(ctx::src).toList()), + true, ctx.floatFormat(0).nameLower() + "_" + name + )); + } + /** * Context for lowering a built-in function call. */ diff --git a/vadl/main/vadl/types/BuiltInTable.java b/vadl/main/vadl/types/BuiltInTable.java index e697b1455..6199bade2 100644 --- a/vadl/main/vadl/types/BuiltInTable.java +++ b/vadl/main/vadl/types/BuiltInTable.java @@ -1043,6 +1043,16 @@ public class BuiltInTable { ///// FLOAT ARITHMETIC ////// + /** + * {@code function fsqrt( t : FloatType, a : Bits, rm : Bits<3> ) -> Bits } + */ + public static final BuiltIn FSQRT = + func("VADL::fsqrt", + Type.relation(List.of(BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returnsFirstBitWidth(BitsType.class) + .build(); + /** * {@code function fadd( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } */ @@ -1069,6 +1079,136 @@ public class BuiltInTable { .returnsFirstBitWidthAndFloatStatus() .build(); + /** + * {@code function fsub( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + */ + public static final BuiltIn FSUB = + func("VADL::fsub", + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstTwoWithSameBitWidthsAndFrm(2) + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fmul( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + */ + public static final BuiltIn FMUL = + func("VADL::fmul", + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstTwoWithSameBitWidthsAndFrm(2) + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fdiv( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + */ + public static final BuiltIn FDIV = + func("VADL::fdiv", + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstTwoWithSameBitWidthsAndFrm(2) + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fmadd( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) + * -> Bits } + */ + public static final BuiltIn FMADD = + func("VADL::fmadd", + Type.relation(List.of( + BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstThreeWithSameBitWidthsAndFrm(3) + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fmsub( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) + * -> Bits } + */ + public static final BuiltIn FMSUB = + func("VADL::fmsub", + Type.relation(List.of( + BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstThreeWithSameBitWidthsAndFrm(3) + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fnmadd( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) + * -> Bits } + */ + public static final BuiltIn FNMADD = + func("VADL::fnmadd", + Type.relation(List.of( + BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstThreeWithSameBitWidthsAndFrm(3) + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fnmsub( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) + * -> Bits } + */ + public static final BuiltIn FNMSUB = + func("VADL::fnmsub", + Type.relation(List.of( + BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstThreeWithSameBitWidthsAndFrm(3) + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fmin( t : FloatType, a : Bits, b : Bits ) -> Bits } + */ + public static final BuiltIn FMIN = + func("VADL::fmin", + Type.relation(List.of(BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstTwoWithSameBitWidths() + .returnsFirstBitWidth(BitsType.class) + .build(); + + /** + * {@code function fmax( t : FloatType, a : Bits, b : Bits ) -> Bits } + */ + public static final BuiltIn FMAX = + func("VADL::fmax", + Type.relation(List.of(BitsType.class, BitsType.class), 1, BitsType.class)) + .takesFirstTwoWithSameBitWidths() + .returnsFirstBitWidth(BitsType.class) + .build(); + + ///// FLOAT COMPARISON ////// + + /** + * {@code function flt( t : FloatType, a : Bits, b : Bits ) -> Bool } + */ + public static final BuiltIn FLT = + func("VADL::flt", + Type.relation(List.of(BitsType.class, BitsType.class), 1, BoolType.class)) + .takesFirstTwoWithSameBitWidths() + .returns(Type.bool()) + .build(); + + /** + * {@code function fle( t : FloatType, a : Bits, b : Bits ) -> Bool } + */ + public static final BuiltIn FLE = + func("VADL::fle", + Type.relation(List.of(BitsType.class, BitsType.class), 1, BoolType.class)) + .takesFirstTwoWithSameBitWidths() + .returns(Type.bool()) + .build(); + + /** + * {@code function feq( t : FloatType, a : Bits, b : Bits ) -> Bool } + */ + public static final BuiltIn FEQ = + func("VADL::feq", + Type.relation(List.of(BitsType.class, BitsType.class), 1, BoolType.class)) + .takesFirstTwoWithSameBitWidths() + .returns(Type.bool()) + .build(); + ///// FLOAT TO INT CONVERSION ////// /** @@ -1166,6 +1306,68 @@ public class BuiltInTable { .returns(bits(32)) .build(); + ///// FLOAT CLASSIFICATION ////// + + /** + * {@code function fisinf( t : FloatType, a : Bits ) -> Bool } + */ + public static final BuiltIn FISINF = + func("VADL::fisinf", + Type.relation(List.of(BitsType.class), 1, BoolType.class)) + .takesDefault() + .returns(Type.bool()) + .build(); + + /** + * {@code function fiszero( t : FloatType, a : Bits ) -> Bool } + */ + public static final BuiltIn FISZERO = + func("VADL::fiszero", + Type.relation(List.of(BitsType.class), 1, BoolType.class)) + .takesDefault() + .returns(Type.bool()) + .build(); + + /** + * {@code function fisneg( t : FloatType, a : Bits ) -> Bool } + */ + public static final BuiltIn FISNEG = + func("VADL::fisneg", + Type.relation(List.of(BitsType.class), 1, BoolType.class)) + .takesDefault() + .returns(Type.bool()) + .build(); + + /** + * {@code function fisdenorm( t : FloatType, a : Bits ) -> Bool } + */ + public static final BuiltIn FISDENORM = + func("VADL::fisdenorm", + Type.relation(List.of(BitsType.class), 1, BoolType.class)) + .takesDefault() + .returns(Type.bool()) + .build(); + + /** + * {@code function fissnan( t : FloatType, a : Bits ) -> Bool } + */ + public static final BuiltIn FISSNAN = + func("VADL::fissnan", + Type.relation(List.of(BitsType.class), 1, BoolType.class)) + .takesDefault() + .returns(Type.bool()) + .build(); + + /** + * {@code function fisqnan( t : FloatType, a : Bits ) -> Bool } + */ + public static final BuiltIn FISQNAN = + func("VADL::fisqnan", + Type.relation(List.of(BitsType.class), 1, BoolType.class)) + .takesDefault() + .returns(Type.bool()) + .build(); + ///// FUNCTIONS ////// @@ -1538,7 +1740,23 @@ private static BuiltIn instr(String name) { ); public static final List FLOAT_ARITHMETIC_BUILT_INS = List.of( - FADD + FSQRT, + FADD, + FSUB, + FMUL, + FDIV, + FMADD, + FMSUB, + FNMADD, + FNMSUB, + FMIN, + FMAX + ); + + public static final List FLOAT_COMPARISON_BUILT_INS = List.of( + FLT, + FLE, + FEQ ); public static final List FLOAT_CONVERSION_BUILT_INS = List.of( @@ -1552,6 +1770,15 @@ private static BuiltIn instr(String name) { FCVTUDF ); + public static final List FLOAT_CLASSIFICATION_BUILT_INS = List.of( + FISINF, + FISZERO, + FISNEG, + FISDENORM, + FISSNAN, + FISQNAN + ); + public static final List FUNCTION_BUILT_INS = List.of( MNEMONIC, CONCATENATE_STRINGS, @@ -1584,7 +1811,9 @@ private static BuiltIn instr(String name) { public static final List FLOAT_BUILT_INS = Stream.of( FLOAT_ARITHMETIC_BUILT_INS.stream(), - FLOAT_CONVERSION_BUILT_INS.stream() + FLOAT_COMPARISON_BUILT_INS.stream(), + FLOAT_CONVERSION_BUILT_INS.stream(), + FLOAT_CLASSIFICATION_BUILT_INS.stream() ).flatMap(s -> s).toList(); public static final List BUILT_INS = Stream.of( @@ -1991,6 +2220,16 @@ public BuiltInBuilder takesFirstTwoWithSameBitWidthsAndFrm(int roundingModeArgId return this; } + public BuiltInBuilder takesFirstThreeWithSameBitWidthsAndFrm(int roundingModeArgIdx) { + takesData((args) -> args.size() > roundingModeArgIdx + && args.get(0).bitWidth() == args.get(1).bitWidth() + && args.get(0).bitWidth() == args.get(2).bitWidth() + && args.get(roundingModeArgIdx).bitWidth() == 3 + ); + this.hasSameBitWidth = true; + return this; + } + public BuiltInBuilder returns(Type returnType) { returns((args) -> returnType); return this; diff --git a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java index 1cba631c1..f4d616f6e 100644 --- a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java @@ -16,6 +16,8 @@ package vadl.utils; +import vadl.viam.graph.dependency.BuiltInCall; + /** * A dispatcher that handles all {@code VADL::*} built-ins. * This dispatcher comes with an empty default implementation for each built-in. @@ -190,10 +192,62 @@ default void handleCTZ(T input) { default void handleCTO(T input) { } + @Override + default void handleFSQRT(T input) { + } + @Override default void handleFADD(T input) { } + @Override + default void handleFSUB(T input) { + } + + @Override + default void handleFMUL(T input) { + } + + @Override + default void handleFDIV(T input) { + } + + @Override + default void handleFMADD(T input) { + } + + @Override + default void handleFMSUB(T input) { + } + + @Override + default void handleFNMADD(T input) { + } + + @Override + default void handleFNMSUB(T input) { + } + + @Override + default void handleFMIN(T input) { + } + + @Override + default void handleFMAX(T input) { + } + + @Override + default void handleFLT(T input) { + } + + @Override + default void handleFLE(T input) { + } + + @Override + default void handleFEQ(T input) { + } + @Override default void handleFCVTFSS(T input) { } @@ -226,6 +280,30 @@ default void handleFCVTUSF(T input) { default void handleFCVTUDF(T input) { } + @Override + default void handleFISINF(T input) { + } + + @Override + default void handleFISZERO(T input) { + } + + @Override + default void handleFISNEG(T input) { + } + + @Override + default void handleFISDENORM(T input) { + } + + @Override + default void handleFISSNAN(T input) { + } + + @Override + default void handleFISQNAN(T input) { + } + @Override default void handleConcat(T input) { } diff --git a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java index c0a78e105..b5f0f6017 100644 --- a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java @@ -113,8 +113,34 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { handleCTZ(input); } else if (builtIn == BuiltInTable.CTO) { handleCTO(input); + } else if (builtIn == BuiltInTable.FSQRT) { + handleFSQRT(input); } else if (builtIn == BuiltInTable.FADD) { handleFADD(input); + } else if (builtIn == BuiltInTable.FSUB) { + handleFSUB(input); + } else if (builtIn == BuiltInTable.FMUL) { + handleFMUL(input); + } else if (builtIn == BuiltInTable.FDIV) { + handleFDIV(input); + } else if (builtIn == BuiltInTable.FMADD) { + handleFMADD(input); + } else if (builtIn == BuiltInTable.FMSUB) { + handleFMSUB(input); + } else if (builtIn == BuiltInTable.FNMADD) { + handleFNMADD(input); + } else if (builtIn == BuiltInTable.FNMSUB) { + handleFNMSUB(input); + } else if (builtIn == BuiltInTable.FMIN) { + handleFMIN(input); + } else if (builtIn == BuiltInTable.FMAX) { + handleFMAX(input); + } else if (builtIn == BuiltInTable.FLT) { + handleFLT(input); + } else if (builtIn == BuiltInTable.FLE) { + handleFLE(input); + } else if (builtIn == BuiltInTable.FEQ) { + handleFEQ(input); } else if (builtIn == BuiltInTable.FCVTFSS) { handleFCVTFSS(input); } else if (builtIn == BuiltInTable.FCVTFSD) { @@ -131,6 +157,18 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { handleFCVTUSF(input); } else if (builtIn == BuiltInTable.FCVTUDF) { handleFCVTUDF(input); + } else if (builtIn == BuiltInTable.FISINF) { + handleFISINF(input); + } else if (builtIn == BuiltInTable.FISZERO) { + handleFISZERO(input); + } else if (builtIn == BuiltInTable.FISNEG) { + handleFISNEG(input); + } else if (builtIn == BuiltInTable.FISDENORM) { + handleFISDENORM(input); + } else if (builtIn == BuiltInTable.FISSNAN) { + handleFISSNAN(input); + } else if (builtIn == BuiltInTable.FISQNAN) { + handleFISQNAN(input); } else if (builtIn == BuiltInTable.CONCATENATE_BITS) { handleConcat(input); } else { @@ -223,8 +261,34 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { void handleCTO(T input); + void handleFSQRT(T input); + void handleFADD(T input); + void handleFSUB(T input); + + void handleFMUL(T input); + + void handleFDIV(T input); + + void handleFMADD(T input); + + void handleFMSUB(T input); + + void handleFNMADD(T input); + + void handleFNMSUB(T input); + + void handleFMIN(T input); + + void handleFMAX(T input); + + void handleFLT(T input); + + void handleFLE(T input); + + void handleFEQ(T input); + void handleFCVTFSS(T input); void handleFCVTFSD(T input); @@ -241,6 +305,18 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { void handleFCVTUDF(T input); + void handleFISINF(T input); + + void handleFISZERO(T input); + + void handleFISNEG(T input); + + void handleFISDENORM(T input); + + void handleFISSNAN(T input); + + void handleFISQNAN(T input); + void handleConcat(T input); } From 73da2527da0c504407853e35d26d4cdfd2963565 Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Sat, 25 Jul 2026 23:36:16 +0200 Subject: [PATCH 07/11] wip: Finish RISC-V D extension - All float necessary built-ins are implemented - ISS target gen works and the QEMU target compiles --- sys/risc-v/rv64d.vadl | 35 ++ sys/risc-v/rv64f.vadl | 349 +--------------- sys/risc-v/rv64fd.vadl | 385 ++++++++++++++++++ .../templates/iss/target/gen-arch/helper.c | 32 +- .../templates/iss/target/gen-arch/helper.h | 24 +- .../passes/common/IssNormalizationPass.java | 30 ++ .../tcg/lowering/TcgOpLoweringPass.java | 38 +- vadl/main/vadl/types/BuiltInTable.java | 81 +++- .../VadlBuiltInEmptyNoStatusDispatcher.java | 24 ++ .../utils/VadlBuiltInNoStatusDispatcher.java | 24 ++ 10 files changed, 647 insertions(+), 375 deletions(-) create mode 100644 sys/risc-v/rv64d.vadl create mode 100644 sys/risc-v/rv64fd.vadl diff --git a/sys/risc-v/rv64d.vadl b/sys/risc-v/rv64d.vadl new file mode 100644 index 000000000..138f13466 --- /dev/null +++ b/sys/risc-v/rv64d.vadl @@ -0,0 +1,35 @@ + +import rv64fd::{RV64IFD} with ("ExtensionFD=D") + +instruction set architecture RV64ID extending RV64IFD = {} + +[ htif ] +processor Spike implements RV64ID = { + constant reset_vec_addr = 0x1000 + + reset = { + PC := reset_vec_addr + } + + [ firmware ] + [ base: 0x80000000 ] + memory region [RAM] DRAM in MEM + + memory region [ROM] MROM in MEM = { + MEM<4>(0x1000) := 0x00000297 // auipc t0, 0x0 + MEM<4>(0x1004) := 0x02828613 // addi a2, t0, 40 + // TODO: this is not quite right: + // this processor has no zicsr extension + MEM<4>(0x1008) := 0x00000013 // addi x0, x0, 0 + MEM<4>(0x100c) := 0x0202b583 // ld a1, 32(t0) + MEM<4>(0x1010) := 0x0182b283 // ld t0, 24(t0) + MEM<4>(0x1014) := 0x00028067 // jr t0 + // store start_addr in memory (0x80000000) + MEM<4>(0x1018) := 0x80000000 // lo32(start_addr) + MEM<4>(0x101c) := 0x00000000 // hi32(start_addr) + // we do not yet support a fdt, but we set the address, + // to keep the registers consistent with upstream + MEM<4>(0x1020) := 0x87e00000 // lo32(fdt_addr) + MEM<4>(0x1024) := 0x00000000 // hi32(fdt_addr) + } +} diff --git a/sys/risc-v/rv64f.vadl b/sys/risc-v/rv64f.vadl index 2941d627a..c7b1b36f8 100644 --- a/sys/risc-v/rv64f.vadl +++ b/sys/risc-v/rv64f.vadl @@ -1,353 +1,10 @@ -import rv64csr::{RV64IMZicsr} +import rv64fd::{RV64IFD} -instruction set architecture RV64IMF extending RV64IMZicsr = { - - model FSize() : Id = {FSize32} - - using ConstTy = UInt<8> - constant FLEN : ConstTy = $FSize - constant FSize32 : ConstTy = 32 - constant FSize64 : ConstTy = 64 - constant FSize128 : ConstTy = 128 - - using FP16 = Bits<16> - using FP32 = Bits<32> - using FP64 = Bits<64> - using FP128 = Bits<128> - - using SIntH = SInt<16> - using UIntH = UInt<16> - using SIntD = SInt<64> - using UIntD = UInt<64> - using SIntQ = SInt<128> - using UIntQ = UInt<128> - - using FRegs = Bits - - using Bits2 = Bits<2> - - register F : Index -> FRegs - - [ sticky fe flag invalid : nv ] - [ sticky fe flag div_by_zero : dz ] - [ sticky fe flag overflow : of ] - [ sticky fe flag underflow : uf ] - [ sticky fe flag inexact : nx ] - register FCSR : FCsrFormat - //alias register fcsr : FCsrFormat = CSR(CsrDefToImpl(CsrDef::fcsr)) - - format FCsrFormat : Bits<32> = - { reserved [31..8] - , frm [7..5] // Rounding mode - , nv [4] // Float exception flag: Invalid operation - , dz [3] // Float exception flag: Division by zero - , of [2] // Float exception flag: Overflow - , uf [1] // Float exception flag: Underflow - , nx [0] // Float exception flag: Inexact - } - - // rounding modes - enumeration Frm : Bits3 = - { rne = 0b000 // Round to Nearest, ties to Even - , rtz = 0b001 // Round to Zero - , rdn = 0b010 // Round Down (towards -infinity) - , rup = 0b011 // Round UP (towards infinity) - , rmm = 0b100 // Round to Nearest, ties to Max Magnitude - // 0b101 // reserved in FRtype.funct3 - // 0b110 // reserved in FRtype.funct3 - , dyn = 0b111 // reserved in FRtype.funct3; in instruction: Dynamic rounding (uses FRtype.funct3) - } - - // format (precision) - enumeration Fmt : Bits2 = - { s = 0b00 // single precision (32 -bit) - , d = 0b01 // double precision (64 -bit) - , h = 0b10 // half precision (16 -bit) - , q = 0b11 // quad precision (128-bit) - } - - function FrmName(frm : Bits3) -> String = - match frm with - { Frm::rne => "rne" - , Frm::rtz => "rtz" - , Frm::rdn => "rdn" - , Frm::rup => "rup" - , Frm::rmm => "rmm" - , _ => "" - } - - format FRtype : Inst = // Rtype register 3 operand instruction format (for float ops) - { funct5 : Bits5 // [31..27] 5 bit function code - , fmt : Bits2 // [26..25] 2 bit format code - , rs2 : Index // [24..20] 2nd source register index / shamt - , rs1 : Index // [19..15] 1st source register index - , funct3 : Bits3 // [14..12] 3 bit function code - , rd : Index // [11..7] destination register index - , opcode : Bits7 // [6..0] 7 bit operation code - , shamt = rs2 as UInt // 5 bit unsigned shift ammount - } - - format R4type : Inst = // R4type register 4 operand instruction format - { rs3 : Bits5 // [31..27] 3rd source register index - , fmt : Bits2 // [26..25] 2 bit format - , rs2 : Index // [24..20] 2nd source register index - , rs1 : Index // [19..15] 1st source register index - , funct3 : Bits3 // [14..12] 3 bit function code - , rd : Index // [11..7] destination register index - , opcode : Bits7 // [6..0] 7 bit operation code - } - - record FRtypeRec (name : Id, mne : Str, rm : Ex, fmt : Ex, funct5 : Bin, instr : Stat) - - model FRtypeInstr (c : FRtypeRec, enc : Encs, asm : IsaDefs) : IsaDefs = { - instruction $c.name : FRtype = $c.instr - encoding $c.name = {opcode = 0b101'0011, funct3 = $c.rm, fmt = $c.fmt, funct5 = $c.funct5, $enc} - $asm - } - - model FRtypeInstr1 (c : FRtypeRec, rs2 : Bin) : IsaDefs = { - $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1))) - } - - model FRtypeInstr1rm (c : FRtypeRec, rs2 : Bin) : IsaDefs = { - $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", FrmName($c.rm))) - } - - model FRtypeInstr2 (c : FRtypeRec) : IsaDefs = { - $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2))) - } - - model FRtypeInstr2rm (c : FRtypeRec) : IsaDefs = { - $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($c.rm))) - } - - [ canonical sNaN : 0x7fa00000 ] - [ canonical qNaN : 0x7fc00000 ] - [ IEEE : 32 ] - float-type IEEE32 - - [ canonical sNaN : 0x7fa00000'00000000 ] - [ canonical qNaN : 0x7fc00000'00000000 ] - [ IEEE : 64 ] - float-type IEEE64 - - record FSizeRec (suffix : Id, iSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, fTy : Id, size : Int) - - // TODO: IEEE16 and IEEE128 are not yet implemented - model FSize16 () : FSizeRec = {(H ; H ; Fmt::h ; FP16 ; SIntH ; UIntH ; IEEE16 ; 16 )} - model FSize32 () : FSizeRec = {(S ; W ; Fmt::s ; FP32 ; SIntW ; UIntW ; IEEE32 ; 32 )} - model FSize64 () : FSizeRec = {(D ; D ; Fmt::d ; FP64 ; SIntD ; UIntD ; IEEE64 ; 64 )} - model FSize128 () : FSizeRec = {(Q ; Q ; Fmt::q ; FP128 ; SIntQ ; UIntQ ; IEEE128 ; 128)} - - function NaNBoxHS(val : FP16) -> FP32 = (0xffff , val) as FP32 - function NaNBoxSD(val : FP32) -> FP64 = (0xffff'ffff , val) as FP64 - function NaNBoxDQ(val : FP64) -> FP128 = (0xffff'ffff'ffff'ffff, val) as FP128 - - model NaNBoxQ (val : Ex) : Ex = { $val } - model NaNBoxD (val : Ex) : Ex = { match : Ex ($FSize = FSize64 => $val; _ => $NaNBoxQ(NaNBoxDQ($val))) } - model NaNBoxS (val : Ex) : Ex = { match : Ex ($FSize = FSize32 => $val; _ => $NaNBoxD(NaNBoxSD($val))) } - model NaNBoxH (val : Ex) : Ex = { $NaNBoxS(NaNBoxHS($val)) } - - model NaNBox (size : FSizeRec, val : Ex) : Ex = { - match : Ex ( - $size.fTy = IEEE16 => $NaNBoxH($val); - $size.fTy = IEEE32 => $NaNBoxS($val); - $size.fTy = IEEE64 => $NaNBoxD($val); - _ => $NaNBoxQ($val) - ) - } - - model-type BoolModelId = (Id, Id) -> Id - model-type BoolModelStr = (Str, Str) -> Str - model UnsignedId (u : Id, s : Id) : Id = { $u } - model SignedId (u : Id, s : Id) : Id = { $s } - model UnsignedStr (u : Str, s : Str) : Str = { $u } - model SignedStr (u : Str, s : Str) : Str = { $s } - record BoolModelRec (id : BoolModelId, str : BoolModelStr) - model Unsigned () : BoolModelRec = {(UnsignedId ; UnsignedStr)} - model Signed () : BoolModelRec = {(SignedId ; SignedStr )} - - model FRtypeInstrBiArith (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr2rm (( - AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; - $rm ; $size.fmt ; $funct5 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty, $rm) in { - F(rd) := $NaNBox($size ; result) - } - )) - } - - model FRtypeInstrMinMax (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr2 (( - AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; - $rm ; $size.fmt ; $funct5 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { - F(rd) := $NaNBox($size ; result) - } - )) - } - - model FRtypeInstrSqrt (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr1rm (( - AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; - $rm ; $size.fmt ; $funct5 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { - F(rd) := $NaNBox($size ; result) - } - ) ; $rs2 ) - } - - model FRtypeInstrCmp (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr2 (( - AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; - $rm ; $size.fmt ; $funct5 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { - X(rd) := result as UIntR // zero extend - } - )) - } - - model FRtypeInstrSgn (name : Id, size : FSizeRec, sgn : Ex, funct5 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr2 (( - AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; - $rm ; $size.fmt ; $funct5 ; - let lower = F(rs1)(($size.size-2)..0) in - let rs1Sgn = F(rs1)($size.size-1) in - let rs2Sgn = F(rs2)($size.size-1) in - F(rd) := $NaNBox($size ; ($sgn, lower) as $size.ty) - )) - } - - model FRtypeInstrMvF2X (name : Id, size : FSizeRec, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr1 (( - AsId($name, "X", $size.iSuffix) ; AsStr($name, ".X.", $size.iSuffix) ; - $rm ; $size.fmt ; $funct5 ; - // first cast to the correct float type and then sign extend - X(rd) := F(rs1) as $size.ty as SIntR - ) ; $rs2 ) - } - - model FRtypeInstrMvX2F (name : Id, size : FSizeRec, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr1 (( - AsId($name, $size.iSuffix, "X") ; AsStr($name, ".", $size.iSuffix, ".X") ; - $rm ; $size.fmt ; $funct5 ; - F(rd) := $NaNBox($size ; X(rs1) as $size.ty) - ) ; $rs2 ) - } - - model FRtypeInstrCvtX2F (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr1rm (( - AsId($name, $size.suffix, $iSize.iSuffix, $u.str("U" ; "")) ; - AsStr($name, ".", $size.suffix, ".", $iSize.iSuffix, $u.str("U" ; "")) ; - $rm ; $size.fmt ; $funct5 ; - let result = $fun($size.fTy, X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), $rm) in { - F(rd) := $NaNBox($size ; result) - } - ) ; $rs2 ) - } - - model FRtypeInstrCvtF2X (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, funct5 : Bin, rs2 : Bin, rm : Ex) : IsaDefs = { - $FRtypeInstr1rm (( - AsId($name, $iSize.iSuffix, $u.str("U" ; ""), $size.suffix) ; - AsStr($name, ".", $iSize.iSuffix, $u.str("U" ; ""), ".", $size.suffix) ; - $rm ; $size.fmt ; $funct5 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { - // always sign extend the result, even unsigned results - X(rd) := result as SIntR - } - ) ; $rs2 ) - } - - model FRtypeInstrClass (name : Id, size : FSizeRec) : IsaDefs = { - instruction AsId($name, $size.suffix) : Rtype = - let f = F(rs1) as $size.ty in - // TODO: this is from QEMU's risc-v vector_helper.c fclass_s(...) - // what predicates should we implement? - let neg = VADL::fisneg($size.fTy, f) in - X(rd) := if VADL::fisinf ($size.fTy, f) then ( if neg then 1 << 0 else 1 << 7 ) else - if VADL::fiszero ($size.fTy, f) then ( if neg then 1 << 3 else 1 << 4 ) else - if VADL::fisdenorm($size.fTy, f) then ( if neg then 1 << 2 else 1 << 5 ) else - if VADL::fissnan ($size.fTy, f) then 1 << 8 else - if VADL::fisqnan ($size.fTy, f) then 1 << 9 else - ( if neg then 1 << 1 else 1 << 6 ) - encoding AsId($name, $size.suffix) = {opcode = 0b101'0011, funct3 = 0b001, rs2 = 0b0'0000, funct7 = 0b111'0000} - assembly AsId($name, $size.suffix) = (AsStr($name), ".", AsStr($size.suffix), " ", register(rd), ",", register(rs1)) - } - - model FLtypeInstr (name : Id, size : FSizeRec, funct3 : Bin) : IsaDefs = { - instruction $name : Itype = - let addr = X(rs1) + immS in - let bytes = $size.size / 8 in - F(rd) := $NaNBox($size ; MEM(addr) as $size.ty) - encoding $name = {opcode = 0b000'0111, funct3 = $funct3} - assembly $name = (mnemonic, " ", register(rd), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") - } - - model FStypeInstr (name : Id, size : FSizeRec, funct3 : Bin) : IsaDefs = { - instruction $name : Stype = - let addr = X(rs1) + immS in - let bytes = $size.size / 8 in - MEM(addr) := F(rs2) as $size.ty - encoding $name = {opcode = 0b010'0111, funct3 = $funct3} - assembly $name = (mnemonic, " ", register(rs2), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") - } - - model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex, opcode : Bin) : IsaDefs = { - instruction AsId($name, $size.suffix) : R4type = - let result = $fun($size.fTy, F(rs1), F(rs2), F(rs3), $rm) in { - F(rd) := $NaNBox($size ; result) - } - encoding AsId($name, $size.suffix) = {opcode = $opcode, funct3 = $rm, fmt = $size.fmt} - assembly AsId($name, $size.suffix) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($rm)) - } - - $FLtypeInstr (FLW ; $FSize32 ; 0b010) - $FStypeInstr (FSW ; $FSize32 ; 0b010) - - $FRtypeInstrBiArith (FADD ; $FSize32 ; VADL::fadd ; 0b0'0000 ; Frm::rne) - $FRtypeInstrBiArith (FSUB ; $FSize32 ; VADL::fsub ; 0b0'0001 ; Frm::rne) - $FRtypeInstrBiArith (FMUL ; $FSize32 ; VADL::fmul ; 0b0'0010 ; Frm::rne) - $FRtypeInstrBiArith (FDIV ; $FSize32 ; VADL::fdiv ; 0b0'0011 ; Frm::rne) - - $FRtypeInstrMinMax (FMIN ; $FSize32 ; VADL::fmin ; 0b0'0101 ; 0b000) - $FRtypeInstrMinMax (FMAX ; $FSize32 ; VADL::fmax ; 0b0'0101 ; 0b001) - - $FRtypeInstrSqrt (FSQRT ; $FSize32 ; VADL::fsqrt ; 0b0'1011 ; 0b0'0000 ; Frm::rne) - - //// TODO: specify somehow that lt, le are signaling and eq is a quiet comparison - $FRtypeInstrCmp (FLE ; $FSize32 ; VADL::fle ; 0b1'0100 ; 0b000) - $FRtypeInstrCmp (FLT ; $FSize32 ; VADL::flt ; 0b1'0100 ; 0b001) - $FRtypeInstrCmp (FEQ ; $FSize32 ; VADL::feq ; 0b1'0100 ; 0b010) - - $FRtypeInstrSgn (FSGNJ ; $FSize32 ; ( rs2Sgn) ; 0b0'0100 ; 0b000) - $FRtypeInstrSgn (FSGNJN ; $FSize32 ; ( 1 - rs2Sgn) ; 0b0'0100 ; 0b001) - $FRtypeInstrSgn (FSGNJX ; $FSize32 ; (rs1Sgn ^ rs2Sgn) ; 0b0'0100 ; 0b010) - - $FRtypeInstrMvF2X (FMV ; $FSize32 ; 0b1'1100 ; 0b000 ; 0b0'0000) - $FRtypeInstrMvX2F (FMV ; $FSize32 ; 0b1'1110 ; 0b000 ; 0b0'0000) - - $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; (SignedId ; SignedStr) ; VADL::fcvtssf ; 0b1'1010 ; 0b0'0000 ; Frm::rne) - $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtusf ; 0b1'1010 ; 0b0'0001 ; Frm::rne) - $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; (SignedId ; SignedStr) ; VADL::fcvtsdf ; 0b1'1010 ; 0b0'0010 ; Frm::rne) - $FRtypeInstrCvtX2F (FCVT ; $FSize32 ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtudf ; 0b1'1010 ; 0b0'0011 ; Frm::rne) - - $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; (SignedId ; SignedStr) ; VADL::fcvtfss ; 0b1'1000 ; 0b0'0000 ; Frm::rne) - $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfus ; 0b1'1000 ; 0b0'0001 ; Frm::rne) - $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; (SignedId ; SignedStr) ; VADL::fcvtfsd ; 0b1'1000 ; 0b0'0010 ; Frm::rne) - $FRtypeInstrCvtF2X (FCVT ; $FSize32 ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfud ; 0b1'1000 ; 0b0'0011 ; Frm::rne) - - $FR4typeInstr (FMADD ; $FSize32 ; VADL::fmadd ; Frm::rne ; 0b100'0011) - $FR4typeInstr (FMSUB ; $FSize32 ; VADL::fmsub ; Frm::rne ; 0b100'0111) - $FR4typeInstr (FNMADD ; $FSize32 ; VADL::fnmadd ; Frm::rne ; 0b100'1111) - $FR4typeInstr (FNMSUB ; $FSize32 ; VADL::fnmsub ; Frm::rne ; 0b100'1011) - - $FRtypeInstrClass (FCLASS ; $FSize32) - -} +instruction set architecture RV64IF extending RV64IFD = {} [ htif ] -processor Spike implements RV64IMF = { +processor Spike implements RV64IF = { constant reset_vec_addr = 0x1000 reset = { diff --git a/sys/risc-v/rv64fd.vadl b/sys/risc-v/rv64fd.vadl new file mode 100644 index 000000000..54b97edc9 --- /dev/null +++ b/sys/risc-v/rv64fd.vadl @@ -0,0 +1,385 @@ + +import rv64csr::{RV64IZicsr} + +instruction set architecture RV64IFD extending RV64IZicsr = { + + // TODO: generate illegal instruction ex + + model ExtensionFD () : Id = {F} + model InstructionsFD (f : IsaDefs, d : IsaDefs) : IsaDefs = { + match : IsaDefs ($ExtensionFD = F => $f; _ => $f $d) + } + + model FSize() : Id = { + match : Id ($ExtensionFD = F => FSize32; _ => FSize64) + } + + using ConstTy = UInt<8> + constant FLEN : ConstTy = $FSize + constant FSize32 : ConstTy = 32 + constant FSize64 : ConstTy = 64 + constant FSize128 : ConstTy = 128 + + using FP16 = Bits<16> + using FP32 = Bits<32> + using FP64 = Bits<64> + using FP128 = Bits<128> + + using SIntH = SInt<16> + using UIntH = UInt<16> + using SIntD = SInt<64> + using UIntD = UInt<64> + using SIntQ = SInt<128> + using UIntQ = UInt<128> + + using FRegs = Bits + + using Bits2 = Bits<2> + + register F : Index -> FRegs + + [ sticky fe flag invalid : nv ] + [ sticky fe flag div_by_zero : dz ] + [ sticky fe flag overflow : of ] + [ sticky fe flag underflow : uf ] + [ sticky fe flag inexact : nx ] + register FCSR : FCsrFormat + //alias register fcsr : FCsrFormat = CSR(CsrDefToImpl(CsrDef::fcsr)) + + format FCsrFormat : Bits<32> = + { reserved [31..8] + , frm [7..5] // Rounding mode + , nv [4] // Float exception flag: Invalid operation + , dz [3] // Float exception flag: Division by zero + , of [2] // Float exception flag: Overflow + , uf [1] // Float exception flag: Underflow + , nx [0] // Float exception flag: Inexact + } + + // rounding modes + enumeration Frm : Bits3 = + { rne = 0b000 // Round to Nearest, ties to Even + , rtz = 0b001 // Round to Zero + , rdn = 0b010 // Round Down (towards -infinity) + , rup = 0b011 // Round UP (towards infinity) + , rmm = 0b100 // Round to Nearest, ties to Max Magnitude + // 0b101 // reserved in FRtype.funct3 + // 0b110 // reserved in FRtype.funct3 + , dyn = 0b111 // reserved in FRtype.funct3; in instruction: Dynamic rounding (uses FRtype.funct3) + } + + // format (precision) + enumeration Fmt : Bits2 = + { s = 0b00 // single precision (32 -bit) + , d = 0b01 // double precision (64 -bit) + , h = 0b10 // half precision (16 -bit) + , q = 0b11 // quad precision (128-bit) + } + + function FrmName(frm : Bits3) -> String = + match frm with + { Frm::rne => "rne" + , Frm::rtz => "rtz" + , Frm::rdn => "rdn" + , Frm::rup => "rup" + , Frm::rmm => "rmm" + , _ => "" + } + + format FRtype : Inst = // Rtype register 3 operand instruction format (for float ops) + { funct5 : Bits5 // [31..27] 5 bit function code + , fmt : Bits2 // [26..25] 2 bit format code + , rs2 : Index // [24..20] 2nd source register index / shamt + , rs1 : Index // [19..15] 1st source register index + , funct3 : Bits3 // [14..12] 3 bit function code + , rd : Index // [11..7] destination register index + , opcode : Bits7 // [6..0] 7 bit operation code + , shamt = rs2 as UInt // 5 bit unsigned shift ammount + } + + format R4type : Inst = // R4type register 4 operand instruction format + { rs3 : Bits5 // [31..27] 3rd source register index + , fmt : Bits2 // [26..25] 2 bit format + , rs2 : Index // [24..20] 2nd source register index + , rs1 : Index // [19..15] 1st source register index + , funct3 : Bits3 // [14..12] 3 bit function code + , rd : Index // [11..7] destination register index + , opcode : Bits7 // [6..0] 7 bit operation code + } + + record FRtypeRec (name : Id, mne : Str, rm : Ex, fmt : Ex, funct5 : Bin, instr : Stat) + + model FRtypeInstr (c : FRtypeRec, enc : Encs, asm : IsaDefs) : IsaDefs = { + instruction $c.name : FRtype = $c.instr + encoding $c.name = {opcode = 0b101'0011, funct3 = $c.rm, fmt = $c.fmt, funct5 = $c.funct5, $enc} + $asm + } + + model FRtypeInstr1 (c : FRtypeRec, rs2 : Bin) : IsaDefs = { + $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1))) + } + + model FRtypeInstr1rm (c : FRtypeRec, rs2 : Bin) : IsaDefs = { + $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", FrmName($c.rm))) + } + + model FRtypeInstr2 (c : FRtypeRec) : IsaDefs = { + $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2))) + } + + model FRtypeInstr2rm (c : FRtypeRec) : IsaDefs = { + $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($c.rm))) + } + + [ canonical sNaN : 0x7fa00000 ] + [ canonical qNaN : 0x7fc00000 ] + [ IEEE : 32 ] + float-type IEEE32 + + [ canonical sNaN : 0x7fa00000'00000000 ] + [ canonical qNaN : 0x7fc00000'00000000 ] + [ IEEE : 64 ] + float-type IEEE64 + + record FSizeRec (suffix : Id, mvSuffix : Id, cvtSuffix : Id, fmt : Ex, ty : Id, sTy : Id, uTy : Id, fTy : Id, size : Int) + + // TODO: IEEE16 and IEEE128 are not yet implemented + // TODO: naming of half and quad (see 'x' values) + model FSize16 () : FSizeRec = {(x ; x ; x ; Fmt::h ; FP16 ; SIntH ; UIntH ; IEEE16 ; 16 )} + model FSize32 () : FSizeRec = {(S ; W ; W ; Fmt::s ; FP32 ; SIntW ; UIntW ; IEEE32 ; 32 )} + model FSize64 () : FSizeRec = {(D ; D ; L ; Fmt::d ; FP64 ; SIntD ; UIntD ; IEEE64 ; 64 )} + model FSize128 () : FSizeRec = {(x ; x ; x ; Fmt::q ; FP128 ; SIntQ ; UIntQ ; IEEE128 ; 128)} + + function NaNBoxHS(val : FP16) -> FP32 = (0xffff , val) as FP32 + function NaNBoxSD(val : FP32) -> FP64 = (0xffff'ffff , val) as FP64 + function NaNBoxDQ(val : FP64) -> FP128 = (0xffff'ffff'ffff'ffff, val) as FP128 + + model NaNBoxQ (val : Ex) : Ex = { $val } + model NaNBoxD (val : Ex) : Ex = { match : Ex ($FSize = FSize64 => $val; _ => $NaNBoxQ(NaNBoxDQ($val))) } + model NaNBoxS (val : Ex) : Ex = { match : Ex ($FSize = FSize32 => $val; _ => $NaNBoxD(NaNBoxSD($val))) } + model NaNBoxH (val : Ex) : Ex = { $NaNBoxS(NaNBoxHS($val)) } + + model NaNBox (size : FSizeRec, val : Ex) : Ex = { + match : Ex ( + $size.fTy = IEEE16 => $NaNBoxH($val); + $size.fTy = IEEE32 => $NaNBoxS($val); + $size.fTy = IEEE64 => $NaNBoxD($val); + _ => $NaNBoxQ($val) + ) + } + + model-type BoolModelId = (Id, Id) -> Id + model-type BoolModelStr = (Str, Str) -> Str + model UnsignedId (u : Id, s : Id) : Id = { $u } + model SignedId (u : Id, s : Id) : Id = { $s } + model UnsignedStr (u : Str, s : Str) : Str = { $u } + model SignedStr (u : Str, s : Str) : Str = { $s } + record BoolModelRec (id : BoolModelId, str : BoolModelStr) + model Unsigned () : BoolModelRec = {(UnsignedId ; UnsignedStr)} + model Signed () : BoolModelRec = {(SignedId ; SignedStr )} + + model FRtypeInstrBiArith (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr2rm (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; $funct5 ; + let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty, $rm) in { + F(rd) := $NaNBox($size ; result) + } + )) + } + + model FRtypeInstrMinMax (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + $FRtypeInstr2 (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; 0b0'0101 ; + let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { + F(rd) := $NaNBox($size ; result) + } + )) + } + + model FRtypeInstrSqrt (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + $FRtypeInstr1rm (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; 0b0'1011 ; + let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { + F(rd) := $NaNBox($size ; result) + } + ) ; 0b0'0000 ) + } + + model FRtypeInstrCmp (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + $FRtypeInstr2 (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; 0b1'0100 ; + let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { + X(rd) := result as UIntR // zero extend + } + )) + } + + model FRtypeInstrSgn (name : Id, size : FSizeRec, sgn : Ex, rm : Ex) : IsaDefs = { + $FRtypeInstr2 (( + AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + $rm ; $size.fmt ; 0b0'0100 ; + let lower = F(rs1)(($size.size-2)..0) in + let rs1Sgn = F(rs1)($size.size-1) in + let rs2Sgn = F(rs2)($size.size-1) in + F(rd) := $NaNBox($size ; ($sgn, lower) as $size.ty) + )) + } + + model FRtypeInstrMvF2X (name : Id, size : FSizeRec) : IsaDefs = { + $FRtypeInstr1 (( + AsId($name, "X", $size.mvSuffix) ; AsStr($name, ".X.", $size.mvSuffix) ; + 0b000 ; $size.fmt ; 0b1'1100 ; + // first cast to the correct float type and then sign extend + X(rd) := F(rs1) as $size.ty as SIntR + ) ; 0b0'0000 ) + } + + model FRtypeInstrMvX2F (name : Id, size : FSizeRec) : IsaDefs = { + $FRtypeInstr1 (( + AsId($name, $size.mvSuffix, "X") ; AsStr($name, ".", $size.mvSuffix, ".X") ; + 0b000 ; $size.fmt ; 0b1'1110 ; + F(rd) := $NaNBox($size ; X(rs1) as $size.ty) + ) ; 0b0'0000 ) + } + + model FRtypeInstrCvtX2F (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1rm (( + AsId($name, $size.suffix, $iSize.cvtSuffix, $u.str("U" ; "")) ; + AsStr($name, ".", $size.suffix, ".", $iSize.mvSuffix, $u.str("U" ; "")) ; + $rm ; $size.fmt ; 0b1'1010 ; + let result = $fun($size.fTy, X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), $rm) in { + F(rd) := $NaNBox($size ; result) + } + ) ; $rs2 ) + } + + model FRtypeInstrCvtF2X (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1rm (( + AsId($name, $iSize.cvtSuffix, $u.str("U" ; ""), $size.suffix) ; + AsStr($name, ".", $iSize.mvSuffix, $u.str("U" ; ""), ".", $size.suffix) ; + $rm ; $size.fmt ; 0b1'1000 ; + let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { + // always sign extend the result, even unsigned results + X(rd) := result as SIntR + } + ) ; $rs2 ) + } + + model FRtypeInstrCvtF2F (name : Id, iSize : FSizeRec, size : FSizeRec, fun : SymEx, rs2 : Bin, rm : Ex) : IsaDefs = { + $FRtypeInstr1rm (( + AsId($name, $size.suffix, $iSize.suffix) ; + AsStr($name, ".", $size.suffix, ".", $iSize.suffix) ; + $rm ; $size.fmt ; 0b0'1000 ; + let result = $fun($iSize.fTy, $size.fTy, F(rs1) as $iSize.ty, $rm) in { + F(rd) := $NaNBox($size ; result) + } + ) ; $rs2 ) + } + + model FRtypeInstrClass (name : Id, size : FSizeRec) : IsaDefs = { + instruction AsId($name, $size.suffix) : FRtype = + let f = F(rs1) as $size.ty in + // TODO: this is from QEMU's risc-v vector_helper.c fclass_s(...) + // what predicates should we implement? + let neg = VADL::fisneg($size.fTy, f) in + X(rd) := if VADL::fisinf ($size.fTy, f) then ( if neg then 1 << 0 else 1 << 7 ) else + if VADL::fiszero ($size.fTy, f) then ( if neg then 1 << 3 else 1 << 4 ) else + if VADL::fisdenorm($size.fTy, f) then ( if neg then 1 << 2 else 1 << 5 ) else + if VADL::fissnan ($size.fTy, f) then 1 << 8 else + if VADL::fisqnan ($size.fTy, f) then 1 << 9 else + ( if neg then 1 << 1 else 1 << 6 ) + encoding AsId($name, $size.suffix) = {opcode = 0b101'0011, funct3 = 0b001, rs2 = 0b0'0000, fmt = $size.fmt, funct5 = 0b111'00} + assembly AsId($name, $size.suffix) = (AsStr($name), ".", AsStr($size.suffix), " ", register(rd), ",", register(rs1)) + } + + model FLtypeInstr (name : Id, size : FSizeRec, funct3 : Bin) : IsaDefs = { + instruction $name : Itype = + let addr = X(rs1) + immS in + let bytes = $size.size / 8 in + F(rd) := $NaNBox($size ; MEM(addr) as $size.ty) + encoding $name = {opcode = 0b000'0111, funct3 = $funct3} + assembly $name = (mnemonic, " ", register(rd), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") + } + + model FStypeInstr (name : Id, size : FSizeRec, funct3 : Bin) : IsaDefs = { + instruction $name : Stype = + let addr = X(rs1) + immS in + let bytes = $size.size / 8 in + MEM(addr) := F(rs2) as $size.ty + encoding $name = {opcode = 0b010'0111, funct3 = $funct3} + assembly $name = (mnemonic, " ", register(rs2), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") + } + + model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex, opcode : Bin) : IsaDefs = { + instruction AsId($name, $size.suffix) : R4type = + let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty, F(rs3) as $size.ty, $rm) in { + F(rd) := $NaNBox($size ; result) + } + encoding AsId($name, $size.suffix) = {opcode = $opcode, funct3 = $rm, fmt = $size.fmt} + assembly AsId($name, $size.suffix) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($rm)) + } + + // TODO: bis (built-in suffix) is a temp solution and will be removed + model CommonRoundedInstrs (size : FSizeRec, rm : Ex, bis : Str) : IsaDefs = { + $FRtypeInstrBiArith (FADD ; $size ; VADL::fadd ; 0b0'0000 ; $rm) + $FRtypeInstrBiArith (FSUB ; $size ; VADL::fsub ; 0b0'0001 ; $rm) + $FRtypeInstrBiArith (FMUL ; $size ; VADL::fmul ; 0b0'0010 ; $rm) + $FRtypeInstrBiArith (FDIV ; $size ; VADL::fdiv ; 0b0'0011 ; $rm) + + $FRtypeInstrSqrt (FSQRT ; $size ; VADL::fsqrt ; $rm) + + $FR4typeInstr (FMADD ; $size ; VADL::fmadd ; $rm ; 0b100'0011) + $FR4typeInstr (FMSUB ; $size ; VADL::fmsub ; $rm ; 0b100'0111) + $FR4typeInstr (FNMADD ; $size ; VADL::fnmadd ; $rm ; 0b100'1111) + $FR4typeInstr (FNMSUB ; $size ; VADL::fnmsub ; $rm ; 0b100'1011) + + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; VADL::AsId(fcvtssf, $bis) ; 0b0'0000 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::AsId(fcvtusf, $bis) ; 0b0'0001 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; VADL::AsId(fcvtsdf, $bis) ; 0b0'0010 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::AsId(fcvtudf, $bis) ; 0b0'0011 ; $rm) + + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; VADL::fcvtfss ; 0b0'0000 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfus ; 0b0'0001 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; VADL::fcvtfsd ; 0b0'0010 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfud ; 0b0'0011 ; $rm) + } + + model CommonInstrs (size : FSizeRec, bis : Str) : IsaDefs = { + // TODO: add other rounding modes + $CommonRoundedInstrs ($size ; Frm::rne ; $bis) + + $FRtypeInstrMinMax (FMIN ; $size ; VADL::fmin ; 0b000) + $FRtypeInstrMinMax (FMAX ; $size ; VADL::fmax ; 0b001) + + //// TODO: specify somehow that lt, le are signaling and eq is a quiet comparison + $FRtypeInstrCmp (FLE ; $size ; VADL::fle ; 0b000) + $FRtypeInstrCmp (FLT ; $size ; VADL::flt ; 0b001) + $FRtypeInstrCmp (FEQ ; $size ; VADL::feq ; 0b010) + + $FRtypeInstrSgn (FSGNJ ; $size ; ( rs2Sgn) ; 0b000) + $FRtypeInstrSgn (FSGNJN ; $size ; ( 1 - rs2Sgn) ; 0b001) + $FRtypeInstrSgn (FSGNJX ; $size ; (rs1Sgn ^ rs2Sgn) ; 0b010) + + $FRtypeInstrMvF2X (FMV ; $size) + $FRtypeInstrMvX2F (FMV ; $size) + + $FRtypeInstrClass (FCLASS ; $size) + } + + $InstructionsFD ( + $FLtypeInstr (FLW ; $FSize32 ; 0b010) + $FStypeInstr (FSW ; $FSize32 ; 0b010) + $CommonInstrs ($FSize32 ; "") + ; + $FLtypeInstr (FLD ; $FSize64 ; 0b011) + $FStypeInstr (FSD ; $FSize64 ; 0b011) + $CommonInstrs ($FSize64 ; "2") + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; VADL::fcvtff2 ; 0b0'0000 ; Frm::rne) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; VADL::fcvtff ; 0b0'0001 ; Frm::rne) + ) + +} diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index e062bff44..9971f05d8 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -104,8 +104,8 @@ void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status #define FLOAT_HELPER_3(S, FMT, NAME, QEMU_FUN, FLAGS) \ uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3) { \ - return f64_fn_3_with_fe_flags(env, float##S##_##QEMU_FUN, FLAGS, \ - &env->fp_status_##FMT, rs1, rs2, rs3); \ + return f64_fn_3_with_fe_flags(env, float##S##_##QEMU_FUN, \ + &env->fp_status_##FMT, FLAGS, rs1, rs2, rs3); \ } #define FLOAT_HELPER_F2I(S, FMT, INT_FMT, NAME) \ @@ -122,6 +122,13 @@ void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status &env->fp_status_##FMT, rs1); \ } +#define FLOAT_HELPER_F2F(S, S2, FMT, FMT2, NAME) \ + uint##S2##_t helper_##FMT##_##FMT2##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + return f64_fn_1_with_fe_flags(env, float##S##_to_##float##S2, \ + &env->fp_status_##FMT, rs1); \ + } + // TODO: optimize fe flags (maybe prep can be omitted; or flags set to avoid recomputation) #define FLOAT_HELPER_CMP(S, FMT, NAME, QEMU_FUN) \ uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ @@ -167,10 +174,23 @@ FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtfsd) FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtfus) FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtfud) -FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtssf) -FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtsdf) -FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtusf) -FLOAT_HELPER_I2F([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtudf) +// FIXME: this is currently very complex and will change +[# th:if="${fmt.bit_size != 64}"] +FLOAT_HELPER_I2F(32, [(${fmt.name})], uint32, fcvtssf) +FLOAT_HELPER_I2F(32, [(${fmt.name})], uint64, fcvtsdf) +FLOAT_HELPER_I2F(32, [(${fmt.name})], uint32, fcvtusf) +FLOAT_HELPER_I2F(32, [(${fmt.name})], uint64, fcvtudf) +[/] +[# th:if="${fmt.bit_size != 32}"] +FLOAT_HELPER_I2F(64, [(${fmt.name})], uint32, fcvtssf2) +FLOAT_HELPER_I2F(64, [(${fmt.name})], uint64, fcvtsdf2) +FLOAT_HELPER_I2F(64, [(${fmt.name})], uint32, fcvtusf2) +FLOAT_HELPER_I2F(64, [(${fmt.name})], uint64, fcvtudf2) +[/] +[# th:each="fmt2 : ${float_formats}"][# th:if="${fmt.name != fmt2.name}"] +[# th:if="${fmt.bit_size != 32}"]FLOAT_HELPER_F2F([(${fmt.bit_size})], 32, [(${fmt.name})], [(${fmt2.name})], fcvtff)[/] +[# th:if="${fmt.bit_size != 64}"]FLOAT_HELPER_F2F([(${fmt.bit_size})], 64, [(${fmt.name})], [(${fmt2.name})], fcvtff2)[/] +[/][/] FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisinf, is_infinity) FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fiszero, is_zero) diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.h b/vadl/main/resources/templates/iss/target/gen-arch/helper.h index e227e32fd..d1ecd51f8 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.h @@ -34,10 +34,24 @@ DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfss, 0, i32, env, i[(${fmt.bit_size})]) DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfsd, 0, i64, env, i[(${fmt.bit_size})]) DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfus, 0, i32, env, i[(${fmt.bit_size})]) DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfud, 0, i64, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtssf, 0, i[(${fmt.bit_size})], env, i32) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtsdf, 0, i[(${fmt.bit_size})], env, i64) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtusf, 0, i[(${fmt.bit_size})], env, i32) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtudf, 0, i[(${fmt.bit_size})], env, i64) + +// FIXME: this is currently very complex and will change +[# th:if="${fmt.bit_size != 64}"] +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtssf, 0, i32, env, i32) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtsdf, 0, i32, env, i64) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtusf, 0, i32, env, i32) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtudf, 0, i32, env, i64) +[/] +[# th:if="${fmt.bit_size != 32}"] +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtssf2, 0, i64, env, i32) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtsdf2, 0, i64, env, i64) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtusf2, 0, i64, env, i32) +DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtudf2, 0, i64, env, i64) +[/] +[# th:each="fmt2 : ${float_formats}"][# th:if="${fmt.name != fmt2.name}"] +[# th:if="${fmt.bit_size != 32}"]DEF_HELPER_FLAGS_2([(${fmt.name})]_[(${fmt2.name})]_fcvtff, 0, i32, env, i[(${fmt.bit_size})])[/] +[# th:if="${fmt.bit_size != 64}"]DEF_HELPER_FLAGS_2([(${fmt.name})]_[(${fmt2.name})]_fcvtff2, 0, i64, env, i[(${fmt.bit_size})])[/] +[/][/] DEF_HELPER_FLAGS_2([(${fmt.name})]_fisinf, 0, i64, env, i[(${fmt.bit_size})]) DEF_HELPER_FLAGS_2([(${fmt.name})]_fiszero, 0, i64, env, i[(${fmt.bit_size})]) @@ -45,4 +59,4 @@ DEF_HELPER_FLAGS_2([(${fmt.name})]_fisneg, 0, i64, env, i[(${fmt.bit_size})]) DEF_HELPER_FLAGS_2([(${fmt.name})]_fisdenorm, 0, i64, env, i[(${fmt.bit_size})]) DEF_HELPER_FLAGS_2([(${fmt.name})]_fissnan, 0, i64, env, i[(${fmt.bit_size})]) DEF_HELPER_FLAGS_2([(${fmt.name})]_fisqnan, 0, i64, env, i[(${fmt.bit_size})]) -[/] \ No newline at end of file +[/] diff --git a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java index 225ea6caf..fcc0f496d 100644 --- a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java +++ b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java @@ -880,6 +880,16 @@ public void handleFEQ(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } + @Override + public void handleFCVTFF(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTFF2(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + @Override public void handleFCVTFSS(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) @@ -920,6 +930,26 @@ public void handleFCVTUDF(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } + @Override + public void handleFCVTSSF2(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTSDF2(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTUSF2(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + + @Override + public void handleFCVTUDF2(BuiltInCall input) { + // do nothing (float ops are done by helper function, which handle everything) + } + @Override public void handleFISINF(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) diff --git a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java index 5a5d1570a..c0ca22e13 100644 --- a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java +++ b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java @@ -1279,14 +1279,20 @@ class BuiltInTcgLoweringExecutor { //// Float to Int Conversion //// - .set(BuiltInTable.FCVTFSS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfss")) - .set(BuiltInTable.FCVTFSD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfsd")) - .set(BuiltInTable.FCVTFUS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfus")) - .set(BuiltInTable.FCVTFUD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfud")) - .set(BuiltInTable.FCVTSSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtssf")) - .set(BuiltInTable.FCVTSDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtsdf")) - .set(BuiltInTable.FCVTUSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtusf")) - .set(BuiltInTable.FCVTUDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtudf")) + .set(BuiltInTable.FCVTFF, (ctx) -> floatHelperCall(ctx, 1, "fcvtff")) + .set(BuiltInTable.FCVTFF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtff2")) + .set(BuiltInTable.FCVTFSS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfss")) + .set(BuiltInTable.FCVTFSD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfsd")) + .set(BuiltInTable.FCVTFUS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfus")) + .set(BuiltInTable.FCVTFUD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfud")) + .set(BuiltInTable.FCVTSSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtssf")) + .set(BuiltInTable.FCVTSDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtsdf")) + .set(BuiltInTable.FCVTUSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtusf")) + .set(BuiltInTable.FCVTUDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtudf")) + .set(BuiltInTable.FCVTSSF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtssf2")) + .set(BuiltInTable.FCVTSDF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtsdf2")) + .set(BuiltInTable.FCVTUSF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtusf2")) + .set(BuiltInTable.FCVTUDF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtudf2")) //// Float Classification //// @@ -1339,8 +1345,9 @@ private static BuiltInResult out(TcgNode... nodes) { private static BuiltInResult floatHelperCall(BuiltInTcgLoweringExecutor.Context ctx, int argc, String name) { return out(new TcgHelperCall( - ctx.dest(), new NodeList<>(IntStream.range(0, argc).mapToObj(ctx::src).toList()), - true, ctx.floatFormat(0).nameLower() + "_" + name + ctx.dest(), new NodeList<>(IntStream.range(0, argc).mapToObj(ctx::src).toList()), true, + ctx.floatFormats().stream().map(FloatFormat::nameLower).collect(Collectors.joining("_")) + + "_" + name )); } @@ -1375,17 +1382,14 @@ private TcgVRefNode src(int index) { } /** - * Retrieves the float format of a float built-in call with the given index. + * Retrieves the float formats of a float built-in call. * - * @param index The index of the float format. - * @return The float format. + * @return A list of the float formats. */ - private FloatFormat floatFormat(int index) { + private List floatFormats() { call.ensure(call instanceof FloatBuiltInCall, "Call is not float built-in"); var floatCall = (FloatBuiltInCall) call; - floatCall.ensure(floatCall.formats().size() > index, - "Tried to access float format %s", index); - return floatCall.formats().get(index); + return floatCall.formats(); } /** diff --git a/vadl/main/vadl/types/BuiltInTable.java b/vadl/main/vadl/types/BuiltInTable.java index 6199bade2..094259cd1 100644 --- a/vadl/main/vadl/types/BuiltInTable.java +++ b/vadl/main/vadl/types/BuiltInTable.java @@ -1209,6 +1209,34 @@ public class BuiltInTable { .returns(Type.bool()) .build(); + ///// FLOAT TO FLOAT CONVERSION ////// + + // FIXME: here the same problem as with int-to-float built-ins + + /** + * Float conversion from float of type t to float of type u. + * {@code function fcvtff( t : FloatType, u : FloatType, a : Bits, rm : Bits<3> ) + * -> Bits<32> } + */ + public static final BuiltIn FCVTFF = + func("VADL::fcvtff", + Type.relation(List.of(BitsType.class, BitsType.class), 2, BitsType.class)) + .takesFrm(1) + .returns(bits(32)) + .build(); + + /** + * Float conversion from float of type t to float of type u. + * {@code function fcvtff2( t : FloatType, u : FloatType, a : Bits, rm : Bits<3> ) + * -> Bits<64> } + */ + public static final BuiltIn FCVTFF2 = + func("VADL::fcvtff2", + Type.relation(List.of(BitsType.class, BitsType.class), 2, BitsType.class)) + .takesFrm(1) + .returns(bits(64)) + .build(); + ///// FLOAT TO INT CONVERSION ////// /** @@ -1261,6 +1289,7 @@ public class BuiltInTable { // float type is? For now, its hardcoded at 32 bits, but it should actually be determined // by the passed float-type. Idea: can the type-checker handle this special case? // We could use type parameters... + // -> for now the following 4 built-ins use hard-coded return-sizes /** * Float conversion from signed single to float. @@ -1306,6 +1335,50 @@ public class BuiltInTable { .returns(bits(32)) .build(); + /** + * Float conversion from signed single to float. + * {@code function fcvtssf2( t : FloatType, a : SInt<32>, rm : Bits<3> ) -> Bits<64> } + */ + public static final BuiltIn FCVTSSF2 = + func("VADL::fcvtssf2", + Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(64)) + .build(); + + /** + * Float conversion from signed double to float. + * {@code function fcvtsdf2( t : FloatType, a : SInt<64>, rm : Bits<3> ) -> Bits<64> } + */ + public static final BuiltIn FCVTSDF2 = + func("VADL::fcvtsdf2", + Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(64)) + .build(); + + /** + * Float conversion from unsigned single to float. + * {@code function fcvtusf2( t : FloatType, a : UInt<32>, rm : Bits<3> ) -> Bits<64> } + */ + public static final BuiltIn FCVTUSF2 = + func("VADL::fcvtusf2", + Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(64)) + .build(); + + /** + * Float conversion from unsigned double to float. + * {@code function fcvtudf2( t : FloatType, a : UInt<64>, rm : Bits<3> ) -> Bits<64> } + */ + public static final BuiltIn FCVTUDF2 = + func("VADL::fcvtudf2", + Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) + .takesFrm(1) + .returns(bits(64)) + .build(); + ///// FLOAT CLASSIFICATION ////// /** @@ -1760,6 +1833,8 @@ private static BuiltIn instr(String name) { ); public static final List FLOAT_CONVERSION_BUILT_INS = List.of( + FCVTFF, + FCVTFF2, FCVTFSS, FCVTFSD, FCVTFUS, @@ -1767,7 +1842,11 @@ private static BuiltIn instr(String name) { FCVTSSF, FCVTSDF, FCVTUSF, - FCVTUDF + FCVTUDF, + FCVTSSF2, + FCVTSDF2, + FCVTUSF2, + FCVTUDF2 ); public static final List FLOAT_CLASSIFICATION_BUILT_INS = List.of( diff --git a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java index f4d616f6e..687849ec5 100644 --- a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java @@ -248,6 +248,14 @@ default void handleFLE(T input) { default void handleFEQ(T input) { } + @Override + default void handleFCVTFF(T input) { + } + + @Override + default void handleFCVTFF2(T input) { + } + @Override default void handleFCVTFSS(T input) { } @@ -280,6 +288,22 @@ default void handleFCVTUSF(T input) { default void handleFCVTUDF(T input) { } + @Override + default void handleFCVTSSF2(T input) { + } + + @Override + default void handleFCVTSDF2(T input) { + } + + @Override + default void handleFCVTUSF2(T input) { + } + + @Override + default void handleFCVTUDF2(T input) { + } + @Override default void handleFISINF(T input) { } diff --git a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java index b5f0f6017..606ae1e51 100644 --- a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java @@ -141,6 +141,10 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { handleFLE(input); } else if (builtIn == BuiltInTable.FEQ) { handleFEQ(input); + } else if (builtIn == BuiltInTable.FCVTFF) { + handleFCVTFF(input); + } else if (builtIn == BuiltInTable.FCVTFF2) { + handleFCVTFF2(input); } else if (builtIn == BuiltInTable.FCVTFSS) { handleFCVTFSS(input); } else if (builtIn == BuiltInTable.FCVTFSD) { @@ -157,6 +161,14 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { handleFCVTUSF(input); } else if (builtIn == BuiltInTable.FCVTUDF) { handleFCVTUDF(input); + } else if (builtIn == BuiltInTable.FCVTSSF2) { + handleFCVTSSF2(input); + } else if (builtIn == BuiltInTable.FCVTSDF2) { + handleFCVTSDF2(input); + } else if (builtIn == BuiltInTable.FCVTUSF2) { + handleFCVTUSF2(input); + } else if (builtIn == BuiltInTable.FCVTUDF2) { + handleFCVTUDF2(input); } else if (builtIn == BuiltInTable.FISINF) { handleFISINF(input); } else if (builtIn == BuiltInTable.FISZERO) { @@ -289,6 +301,10 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { void handleFEQ(T input); + void handleFCVTFF(T input); + + void handleFCVTFF2(T input); + void handleFCVTFSS(T input); void handleFCVTFSD(T input); @@ -305,6 +321,14 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { void handleFCVTUDF(T input); + void handleFCVTSSF2(T input); + + void handleFCVTSDF2(T input); + + void handleFCVTUSF2(T input); + + void handleFCVTUDF2(T input); + void handleFISINF(T input); void handleFISZERO(T input); From b3e41cdc4f2b8c99c2c56ca74209e0dcdaa7cc5c Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Mon, 27 Jul 2026 11:43:26 +0200 Subject: [PATCH 08/11] wip: Add risc-v cosim scripts and config --- .../resources/cosim_configs/riscv_config.toml | 181 ++++++++++++++++++ .../resources/cosim_scripts/riscv/compiler.py | 110 +++++++++++ .../resources/cosim_scripts/riscv/main.py | 91 +++++++++ 3 files changed, 382 insertions(+) create mode 100644 vadl-test/resources/cosim_configs/riscv_config.toml create mode 100644 vadl-test/resources/cosim_scripts/riscv/compiler.py create mode 100644 vadl-test/resources/cosim_scripts/riscv/main.py diff --git a/vadl-test/resources/cosim_configs/riscv_config.toml b/vadl-test/resources/cosim_configs/riscv_config.toml new file mode 100644 index 000000000..de66bfc63 --- /dev/null +++ b/vadl-test/resources/cosim_configs/riscv_config.toml @@ -0,0 +1,181 @@ +# SPDX-FileCopyrightText : © 2025 TU Wien +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# General settings for the QEMU plugin system which set up the co-simulation +[qemu] + +# The path to the compiled cosimulation qemu-plugin +plugin="/opt/qemu/lib/qemu/libcosimulation.so" + +# Whether to ignore registers that are not defined by a [qemu.gdb_reg_map] mapping +# This option is useful to test the current implementation of a simulator which might not yet have all registers implemented against another (complete) implementation +# Filters in conjunction with `ignore_registers` +ignore_unset_registers = true + +# Ignore specific registers +# Filters in conjunction with `ignore_unset_registers` +ignore_registers = [] + + +# Defines a list of clients to test against +# A single client is also possible, e.g. to check if a crash or similar occurs +# In most use-cases, 2 clients (one test-simulator and one reference-simulator) are used +[[qemu.clients]] + +# Optional: A custom name for a client +# Will default to the index of the client in this list +name = "VADL" + +# The executable of the ISS +exec = "/opt/qemu/bin/qemu-system-rv64imf" + +# The path to the compiled file to use for testing +test_exec="./test-execs/test_vadl" + +# "bios" | "kernel": Defines where the test-executable is passed to when starting the QEMU-client +pass_test_exec_to = "bios" + +# Applies additional arguments to the ISS-executable, e.g. `qemu-system-riscv64 -nographic -d plugin` +additional_args = [ + "-plugin", + "/opt/qemu/lib/qemu/libstoptrigger.so,addr=0x80000010", + "-nographic", + "-d", "plugin" +] + +# A hint for the cosimulator whether the client uses little or big endian +# This setting does not change the execution in any way and is only used in case clients have differing endianess +# but should still be compared "correctly" +# This field is optional, not setting it means the register-data is being compared left-to-right (i.e. big-endian by default) +# This setting also *does not* have an effect on tracing, data will still be stored in the original endianess + +# This setting must match the `TARGET_BIG_ENDIAN` definition in `gen-arch-softmmu.mak` +# If the vadl memory definition is annotated with `[ big endian : Condition ]`, this setting should be set to +# "big" or be ommitted. If it is annotated with `[ little endian : Condition ]` or not annotated with any +# of the two, this setting should be explicitly set to "little". +endian = "little" + +# The following three options configure which instructions from the `test_exec` executable should actually be tested. +# NOTE: Only applies to `layer = "insn" | "tb-strict"` +# NOTE: This option is must be set per client to be able to account for different setup-codes per ISS +# Skips the first n instructions + +skip_n_instructions = 0 + +# Settings for running the qemu-clients with gdb-debugging enabled. +# Use these settings when possible instead of configuring the flags in the additional_args array since otherwise the runner will mark the client as finished because it did not respond in time. +[qemu.clients.gdb] +enable = false +# The target_type can be optionally set ("chardev" | "port"), default is "chardev" +# For reference see the QEMU GDB usage documentation: https://qemu-project.gitlab.io/qemu/system/gdb.html +# For more info regarding chardev/unix sockets see: https://qemu-project.gitlab.io/qemu/system/gdb.html#using-unix-sockets +# Using a chardev is preferred to ensure that the target is always available for cosimulation +# NOTE: It is not necessary (and will lead to errors if done anyway) to manually create the char-device, simply use a path that is available and +# QEMU will automatically handle creating the device-file. +# If target_type is "port" then simply enter the port (e.g. "tcp:1234") into the remote_target field, QEMU will try to listen on the port automatically +target_type = "chardev" +remote_target = "/tmp/gdb-cosim-VADL" + +[[qemu.clients]] +name = "UPSTREAM" + +exec = "/opt/qemu/bin/qemu-system-riscv64" + +test_exec="./test-execs/test_upstream" + +pass_test_exec_to = "bios" + +additional_args = [ + "-plugin", + "/opt/qemu/lib/qemu/libstoptrigger.so,addr=0x80000010", + "-nographic", + "-M", "virt", + "-d", "plugin" +] + +endian = "little" + +skip_n_instructions = 9 + +[qemu.clients.gdb] +enable = false +target_type = "chardev" +remote_target = "/tmp/gdb-cosim-VADL" + + +# Defines a custom map where the key (e.g. x0) is mapped to another value (e.g. zero) +[qemu.gdb_reg_map] +pc = "pc" + +# Defines the test-source and how to test +[testing] + +# The testing-protocol defines how the clients are run and tested against eachother +[testing.protocol] +# Defines an *execution-step* of a test-run. +# "insn": The execution-step is the *execution of a single instruction*, e.g. `addi t4,zero,2`. +# This layer is independent of how an ISS generates translation-blocks for qemu. +# This is the most thorough but also slowest option. +# +# NOTE: the following is not yet implemented. +# "tb": The execution-step is the *execution of a single or multiple translation-blocks*. +# Multiple translation-blocks might be executed in a single step if another client executed a larger. +# (but potentially equivalent to multiple smaller TBs) translation-block. +# This allows instruction-equivalent clients to "synchronize" even if the generated translation-blocks differ. +# This option is faster than "insn" but less thorough. +# +# "tb-strict": The execution-step is the *execution of a single translation-blocks*. +# The same as "tb" but without the synchronization logic. Meaning that equal translation-blocks are assumed. +# This option is useful if the instructions of the ISS are already correct and the TB-Block generator needs to be tested. +layer = "insn" + +# Whether reads/writes from/to memory should also be compared +with_memory_checks = true + +# "lockstep": All clients are run and compared one *execution-step* at a time. +# This means that the test will exit on the first divergence (or at the end if no diffs where found) +# Currently, this is the only implemented mode. +mode = "lockstep" + + +# Execute all remaining instructions (overrides `stop_after_n_instructions` if set to true) +execute_all_remaining_instructions = false + +# Execute the next (after skipped) n instructions +stop_after_n_instructions = 100 + +# Where the test result should be saved and in which format +[testing.protocol.out] +# file = "./cosim-run/result/result.json" + +# "short" | "full" +verbosity = "full" + +[logging] +enable = true +# tracing log-levels as defined here: https://docs.rs/tracing/latest/tracing/struct.Level.html#implementations +level = "trace" + +# The directory will also contain files for the stdout and stderr of each client +dir = "." +file = "cosim.log" + +# Clears the logfile every time the program is run +clear_on_rerun = true + +[dev] +# Prints the loaded configuration if set to true and exits +dry_run = false diff --git a/vadl-test/resources/cosim_scripts/riscv/compiler.py b/vadl-test/resources/cosim_scripts/riscv/compiler.py new file mode 100644 index 000000000..078ddf92a --- /dev/null +++ b/vadl-test/resources/cosim_scripts/riscv/compiler.py @@ -0,0 +1,110 @@ +import os +import subprocess +from pathlib import Path + +AS = "riscv64-unknown-elf-as" +LD = "riscv64-unknown-elf-ld" +OBJDUMP = "riscv64-unknown-elf-objdump" + +def compile(id: str, asm: str, debug: bool = True) -> dict: + asm_path = build_assembly(id, asm) + linker_path = build_linker_script(id) + + obj = _tmp_file(id, f"obj-{id}.o") + elf = _tmp_file(id, f"elf-{id}") + assemble(AS, asm_path, obj) + link(LD, linker_path, obj, elf) + + result = { + "asm": asm_path, + "lnscript": linker_path, + "obj": obj, + "elf": elf + } + + if debug: + objdump_file = _tmp_file(id, f"elf-{id}.dump") + objdump(OBJDUMP, elf, objdump_file) + result.update({ + "objdump": objdump_file + }) + + return result + + +def assemble(as_cmd: str, asm_path: Path, obj_out: Path) -> None: + proc = subprocess.run([ + # TODO: check if these first two args are ok + as_cmd, "-march=rv64imd", "-mabi=lp64", "-o", str(obj_out), str(asm_path)], + ) + if proc.returncode != 0: + raise RuntimeError(f"Assembly failed ({as_cmd}): {proc.stderr.decode()}") + +def link(ld_cmd: str, linker_script: Path, obj_in: Path, elf_out: Path) -> None: + proc = subprocess.run([ + ld_cmd, "-T", str(linker_script), "-o", str(elf_out), str(obj_in)], + ) + if proc.returncode != 0: + raise RuntimeError(f"Linking failed ({ld_cmd}): {proc.stderr.decode()}") + +def build_assembly(id: str, core: str) -> Path: + asm_out = _tmp_file(id, f"asm-{id}.s") + + # We load the tests into the RAM region at 0x40000000, because loading into the + # firmware region does not work, because aarch32/virt.vadl already has firmware. + + content = f""" + .globl _start + .section .text + _start: + addiw t0,zero,1 # load address 0x8000000014 + slli t0,t0,0x1f + addi t0,t0,0x14 + jr t0 + nop # here is 0x8000000010 + {core} # here is 0x8000000014 + addiw t0,zero,1 # load address 0x8000000010 + slli t0,t0,0x1f + addi t0,t0,0x10 + # Jump to nop triggers simulation termination (stoptrigger plugin) + jr t0 + """ + with open(asm_out, "w") as f: + f.write(content) + return asm_out + + +def build_linker_script(id: str) -> Path: + linker_out = _tmp_file(id, f"linker-{id}.ld") + + content = """ + ENTRY(_start) + + PHDRS + { + text_seg PT_LOAD FLAGS(5); /* R + X = 4 + 1 */ + } + + SECTIONS + { + . = 0x80000000; + .text ALIGN(4) : { *(.text) } :text_seg + } + """ + with open(linker_out, "w") as f: + f.write(content) + return linker_out + +def objdump(objdump_bin: str, obj_file: Path, out_file: Path): + with out_file.open("wb") as f: + proc = subprocess.run( + [objdump_bin, "-D", str(obj_file)], + stdout=f, + ) + if proc.returncode != 0: + raise RuntimeError(proc.stderr.decode()) + +def _tmp_file(id: str, name: str) -> Path: + build_dir = f"/tmp/build-{id}/" + os.makedirs(build_dir, exist_ok=True) + return Path(f"{build_dir}/{name}") diff --git a/vadl-test/resources/cosim_scripts/riscv/main.py b/vadl-test/resources/cosim_scripts/riscv/main.py new file mode 100644 index 000000000..cee859084 --- /dev/null +++ b/vadl-test/resources/cosim_scripts/riscv/main.py @@ -0,0 +1,91 @@ +import argparse +from pathlib import Path +import subprocess +from concurrent.futures import ProcessPoolExecutor, as_completed +import yaml +import os +import compiler +import shutil +import sys +import threading + +def dump_debug_info(tid: str, results: Path, comp: dict): + debug_dir = results / f"{tid}_debug" + os.makedirs(debug_dir, exist_ok=True) + shutil.copy(comp["asm"], debug_dir) + shutil.copy(comp["lnscript"], debug_dir) + shutil.copy(comp["elf"], debug_dir) + shutil.copy(comp["objdump"], debug_dir) + +def run_cosim(elf: str, out: Path, cosim_config: Path): + e = os.environ.copy() + e["RUST_BACKTRACE"] = "1" + subprocess.run([ + "vadl-cosim-broker", + "--config", cosim_config, + "--test-exec", elf, + "--output-file", str(out) + ], + env=e, + ) + +def compile_test(t: dict, results: Path) -> dict: + tid = str(t["id"]) + debug = t["debug"] + comp = compiler.compile(tid, str(t["asm_core"]), debug) + if debug: + dump_debug_info(tid, results, comp) + return comp + +def run_test(t: dict, results: Path, cosim_config: Path): + tid = t["id"] + comp = compile_test(t, results) + try: + run_cosim(str(comp["elf"]), results / f"result-{tid}", cosim_config) + except Exception as e: + print(f"error for test=\"{tid}\": ", e) + +def report_progress(completed: int, total: int): + width = 30 + filled = width if total == 0 else int(width * completed / total) + bar = "#" * filled + "-" * (width - filled) + print(f"[{bar}] {completed}/{total}", file=sys.stderr, flush=True) + +def main(testsuite_path: Path): + config = yaml.safe_load(testsuite_path.read_text()) + results = Path(config.get("result_dir", "/work/results")) + results.mkdir(parents=True, exist_ok=True) + + num_cores = os.cpu_count() + if num_cores is None: + num_cores = 1 # safe fallback + + cosim_config = config.get("cosim_config", "/cosim_config/riscv_config.toml") + tests = config.get("tests", []) + total_tests = len(tests) + + if total_tests == 0: + report_progress(0, 0) + return + + with ProcessPoolExecutor(num_cores) as executor: + futures = [ + executor.submit(run_test, t, results, cosim_config) + for t in tests + ] + + report_progress(0, total_tests) + completed = 0 + lock = threading.Lock() + for future in as_completed(futures): + future.result() + with lock: + completed += 1 + if completed % 100 == 0: + report_progress(completed, total_tests) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("config") + args = parser.parse_args() + main(Path(args.config)) From d23227e96fbbb9169e2adc45f4d7e0248cada738 Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Wed, 29 Jul 2026 00:39:06 +0200 Subject: [PATCH 09/11] wip: Add built-in constant parameters - Enables specifying constant parameters for built-ins like this: VADL::builtin(params, ...) --- sys/risc-v/rv64fd.vadl | 115 ++-- .../main/vadl/ast/AnnotationTable.java | 1 + .../main/vadl/ast/BehaviorLowering.java | 67 +- .../main/vadl/ast/ConstantEvaluator.java | 10 +- .../main/vadl/ast/MacroExpander.java | 7 +- vadl-frontend/main/vadl/ast/TypeChecker.java | 131 ++-- vadl-frontend/main/vadl/ast/Ungrouper.java | 2 +- .../main/vadl/ast/nodes/CallIndexExpr.java | 6 +- .../vadl/ast/nodes/FloatTypeDefinition.java | 4 + .../main/vadl/ast/nodes/IsCallExpr.java | 3 +- vadl-frontend/main/vadl/ast/nodes/IsId.java | 4 +- .../main/vadl/ast/nodes/IsSymExpr.java | 3 +- .../main/vadl/ast/nodes/SymbolExpr.java | 36 +- .../main/vadl/ast/nodes/TypeLiteral.java | 4 +- vadl-frontend/main/vadl/ast/vadl.ATG | 15 +- .../resources/cosim_configs/riscv_config.toml | 73 ++- .../templates/iss/target/gen-arch/cpu.c | 4 - .../templates/iss/target/gen-arch/helper.c | 184 ++---- .../templates/iss/target/gen-arch/helper.h | 73 +-- vadl/main/vadl/dump/InfoUtils.java | 3 +- .../common/IssFloatBuiltinCollectionPass.java | 111 ++++ .../passes/common/IssNormalizationPass.java | 55 +- .../tcg/lowering/TcgOpLoweringPass.java | 47 +- .../template/IssTemplateRenderingPass.java | 41 +- vadl/main/vadl/pass/order/IssPassOrder.java | 4 +- vadl/main/vadl/types/BuiltInTable.java | 612 +++++++++--------- vadl/main/vadl/types/RelationType.java | 48 +- vadl/main/vadl/types/Type.java | 43 +- .../VadlBuiltInEmptyNoStatusDispatcher.java | 46 +- .../utils/VadlBuiltInNoStatusDispatcher.java | 66 +- vadl/main/vadl/viam/Constant.java | 87 ++- .../viam/graph/dependency/BuiltInCall.java | 48 +- .../graph/dependency/FloatBuiltInCall.java | 106 --- 33 files changed, 1079 insertions(+), 980 deletions(-) create mode 100644 vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java delete mode 100644 vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java diff --git a/sys/risc-v/rv64fd.vadl b/sys/risc-v/rv64fd.vadl index 54b97edc9..51c729ee6 100644 --- a/sys/risc-v/rv64fd.vadl +++ b/sys/risc-v/rv64fd.vadl @@ -182,9 +182,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr2rm (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; $funct5 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty, $rm) in { - F(rd) := $NaNBox($size ; result) - } + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, $rm)) )) } @@ -192,9 +190,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b0'0101 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { - F(rd) := $NaNBox($size ; result) - } + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty)) )) } @@ -202,9 +198,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr1rm (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b0'1011 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { - F(rd) := $NaNBox($size ; result) - } + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, $rm)) ) ; 0b0'0000 ) } @@ -212,9 +206,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b1'0100 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty) in { - X(rd) := result as UIntR // zero extend - } + X(rd) := VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty) as UIntR // zero extend )) } @@ -251,9 +243,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { AsId($name, $size.suffix, $iSize.cvtSuffix, $u.str("U" ; "")) ; AsStr($name, ".", $size.suffix, ".", $iSize.mvSuffix, $u.str("U" ; "")) ; $rm ; $size.fmt ; 0b1'1010 ; - let result = $fun($size.fTy, X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), $rm) in { - F(rd) := $NaNBox($size ; result) - } + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), $rm)) ) ; $rs2 ) } @@ -262,10 +252,8 @@ instruction set architecture RV64IFD extending RV64IZicsr = { AsId($name, $iSize.cvtSuffix, $u.str("U" ; ""), $size.suffix) ; AsStr($name, ".", $iSize.mvSuffix, $u.str("U" ; ""), ".", $size.suffix) ; $rm ; $size.fmt ; 0b1'1000 ; - let result = $fun($size.fTy, F(rs1) as $size.ty, $rm) in { - // always sign extend the result, even unsigned results - X(rd) := result as SIntR - } + // always sign extend the result, even unsigned results + X(rd) := VADL::$fun<$size.fTy, $iSize.size>(F(rs1) as $size.ty, $rm) as SIntR ) ; $rs2 ) } @@ -274,9 +262,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { AsId($name, $size.suffix, $iSize.suffix) ; AsStr($name, ".", $size.suffix, ".", $iSize.suffix) ; $rm ; $size.fmt ; 0b0'1000 ; - let result = $fun($iSize.fTy, $size.fTy, F(rs1) as $iSize.ty, $rm) in { - F(rd) := $NaNBox($size ; result) - } + F(rd) := $NaNBox($size ; VADL::$fun<$iSize.fTy, $size.fTy>(F(rs1) as $iSize.ty, $rm)) ) ; $rs2 ) } @@ -285,13 +271,13 @@ instruction set architecture RV64IFD extending RV64IZicsr = { let f = F(rs1) as $size.ty in // TODO: this is from QEMU's risc-v vector_helper.c fclass_s(...) // what predicates should we implement? - let neg = VADL::fisneg($size.fTy, f) in - X(rd) := if VADL::fisinf ($size.fTy, f) then ( if neg then 1 << 0 else 1 << 7 ) else - if VADL::fiszero ($size.fTy, f) then ( if neg then 1 << 3 else 1 << 4 ) else - if VADL::fisdenorm($size.fTy, f) then ( if neg then 1 << 2 else 1 << 5 ) else - if VADL::fissnan ($size.fTy, f) then 1 << 8 else - if VADL::fisqnan ($size.fTy, f) then 1 << 9 else - ( if neg then 1 << 1 else 1 << 6 ) + let neg = VADL::fisneg<$size.fTy>(f) in + X(rd) := if VADL::fisinf <$size.fTy>(f) then ( if neg then 1 << 0 else 1 << 7 ) else + if VADL::fiszero <$size.fTy>(f) then ( if neg then 1 << 3 else 1 << 4 ) else + if VADL::fisdenorm<$size.fTy>(f) then ( if neg then 1 << 2 else 1 << 5 ) else + if VADL::fissnan <$size.fTy>(f) then 1 << 8 else + if VADL::fisqnan <$size.fTy>(f) then 1 << 9 else + ( if neg then 1 << 1 else 1 << 6 ) encoding AsId($name, $size.suffix) = {opcode = 0b101'0011, funct3 = 0b001, rs2 = 0b0'0000, fmt = $size.fmt, funct5 = 0b111'00} assembly AsId($name, $size.suffix) = (AsStr($name), ".", AsStr($size.suffix), " ", register(rd), ",", register(rs1)) } @@ -316,49 +302,46 @@ instruction set architecture RV64IFD extending RV64IZicsr = { model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex, opcode : Bin) : IsaDefs = { instruction AsId($name, $size.suffix) : R4type = - let result = $fun($size.fTy, F(rs1) as $size.ty, F(rs2) as $size.ty, F(rs3) as $size.ty, $rm) in { - F(rd) := $NaNBox($size ; result) - } + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, F(rs3) as $size.ty, $rm)) encoding AsId($name, $size.suffix) = {opcode = $opcode, funct3 = $rm, fmt = $size.fmt} assembly AsId($name, $size.suffix) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($rm)) } - // TODO: bis (built-in suffix) is a temp solution and will be removed - model CommonRoundedInstrs (size : FSizeRec, rm : Ex, bis : Str) : IsaDefs = { - $FRtypeInstrBiArith (FADD ; $size ; VADL::fadd ; 0b0'0000 ; $rm) - $FRtypeInstrBiArith (FSUB ; $size ; VADL::fsub ; 0b0'0001 ; $rm) - $FRtypeInstrBiArith (FMUL ; $size ; VADL::fmul ; 0b0'0010 ; $rm) - $FRtypeInstrBiArith (FDIV ; $size ; VADL::fdiv ; 0b0'0011 ; $rm) - - $FRtypeInstrSqrt (FSQRT ; $size ; VADL::fsqrt ; $rm) - - $FR4typeInstr (FMADD ; $size ; VADL::fmadd ; $rm ; 0b100'0011) - $FR4typeInstr (FMSUB ; $size ; VADL::fmsub ; $rm ; 0b100'0111) - $FR4typeInstr (FNMADD ; $size ; VADL::fnmadd ; $rm ; 0b100'1111) - $FR4typeInstr (FNMSUB ; $size ; VADL::fnmsub ; $rm ; 0b100'1011) - - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; VADL::AsId(fcvtssf, $bis) ; 0b0'0000 ; $rm) - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::AsId(fcvtusf, $bis) ; 0b0'0001 ; $rm) - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; VADL::AsId(fcvtsdf, $bis) ; 0b0'0010 ; $rm) - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::AsId(fcvtudf, $bis) ; 0b0'0011 ; $rm) - - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; VADL::fcvtfss ; 0b0'0000 ; $rm) - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfus ; 0b0'0001 ; $rm) - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; VADL::fcvtfsd ; 0b0'0010 ; $rm) - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; VADL::fcvtfud ; 0b0'0011 ; $rm) + model CommonRoundedInstrs (size : FSizeRec, rm : Ex) : IsaDefs = { + $FRtypeInstrBiArith (FADD ; $size ; fadd ; 0b0'0000 ; $rm) + $FRtypeInstrBiArith (FSUB ; $size ; fsub ; 0b0'0001 ; $rm) + $FRtypeInstrBiArith (FMUL ; $size ; fmul ; 0b0'0010 ; $rm) + $FRtypeInstrBiArith (FDIV ; $size ; fdiv ; 0b0'0011 ; $rm) + + $FRtypeInstrSqrt (FSQRT ; $size ; fsqrt ; $rm) + + $FR4typeInstr (FMADD ; $size ; fmadd ; $rm ; 0b100'0011) + $FR4typeInstr (FMSUB ; $size ; fmsub ; $rm ; 0b100'0111) + $FR4typeInstr (FNMADD ; $size ; fnmadd ; $rm ; 0b100'1111) + $FR4typeInstr (FNMSUB ; $size ; fnmsub ; $rm ; 0b100'1011) + + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; fcvtsf ; 0b0'0000 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; fcvtuf ; 0b0'0001 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; fcvtsf ; 0b0'0010 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; fcvtuf ; 0b0'0011 ; $rm) + + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; fcvtfs ; 0b0'0000 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; fcvtfu ; 0b0'0001 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; fcvtfs ; 0b0'0010 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; fcvtfu ; 0b0'0011 ; $rm) } - model CommonInstrs (size : FSizeRec, bis : Str) : IsaDefs = { + model CommonInstrs (size : FSizeRec) : IsaDefs = { // TODO: add other rounding modes - $CommonRoundedInstrs ($size ; Frm::rne ; $bis) + $CommonRoundedInstrs ($size ; Frm::rne) - $FRtypeInstrMinMax (FMIN ; $size ; VADL::fmin ; 0b000) - $FRtypeInstrMinMax (FMAX ; $size ; VADL::fmax ; 0b001) + $FRtypeInstrMinMax (FMIN ; $size ; fmin ; 0b000) + $FRtypeInstrMinMax (FMAX ; $size ; fmax ; 0b001) //// TODO: specify somehow that lt, le are signaling and eq is a quiet comparison - $FRtypeInstrCmp (FLE ; $size ; VADL::fle ; 0b000) - $FRtypeInstrCmp (FLT ; $size ; VADL::flt ; 0b001) - $FRtypeInstrCmp (FEQ ; $size ; VADL::feq ; 0b010) + $FRtypeInstrCmp (FLE ; $size ; fle ; 0b000) + $FRtypeInstrCmp (FLT ; $size ; flt ; 0b001) + $FRtypeInstrCmp (FEQ ; $size ; feq ; 0b010) $FRtypeInstrSgn (FSGNJ ; $size ; ( rs2Sgn) ; 0b000) $FRtypeInstrSgn (FSGNJN ; $size ; ( 1 - rs2Sgn) ; 0b001) @@ -373,13 +356,13 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $InstructionsFD ( $FLtypeInstr (FLW ; $FSize32 ; 0b010) $FStypeInstr (FSW ; $FSize32 ; 0b010) - $CommonInstrs ($FSize32 ; "") + $CommonInstrs ($FSize32) ; $FLtypeInstr (FLD ; $FSize64 ; 0b011) $FStypeInstr (FSD ; $FSize64 ; 0b011) - $CommonInstrs ($FSize64 ; "2") - $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; VADL::fcvtff2 ; 0b0'0000 ; Frm::rne) - $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; VADL::fcvtff ; 0b0'0001 ; Frm::rne) + $CommonInstrs ($FSize64) + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; fcvt ; 0b0'0000 ; Frm::rne) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; fcvt ; 0b0'0001 ; Frm::rne) ) } diff --git a/vadl-frontend/main/vadl/ast/AnnotationTable.java b/vadl-frontend/main/vadl/ast/AnnotationTable.java index 9116b6dac..a169b9ba4 100644 --- a/vadl-frontend/main/vadl/ast/AnnotationTable.java +++ b/vadl-frontend/main/vadl/ast/AnnotationTable.java @@ -338,6 +338,7 @@ public class AnnotationTable { /// FLOAT RELATED /// annotationOn(FloatTypeDefinition.class, "IEEE", ConstantAnnotation::new) + .applyAst((def, annotation) -> def.size = annotation.constant.value().intValue()) .applyViam((def, annotation, lowering) -> { var encoding = FloatFormat.Encoding.ieee(annotation.constant.value().intValue()); ensure(encoding != null, diff --git a/vadl-frontend/main/vadl/ast/BehaviorLowering.java b/vadl-frontend/main/vadl/ast/BehaviorLowering.java index 78f0592d4..489a1c062 100644 --- a/vadl-frontend/main/vadl/ast/BehaviorLowering.java +++ b/vadl-frontend/main/vadl/ast/BehaviorLowering.java @@ -126,7 +126,6 @@ import vadl.utils.BigIntUtils; import vadl.utils.Either; import vadl.utils.Pair; -import vadl.utils.SourceLocation; import vadl.utils.WithLocation; import vadl.viam.ArtificialResource; import vadl.viam.Constant; @@ -167,7 +166,6 @@ import vadl.viam.graph.dependency.ExpressionNode; import vadl.viam.graph.dependency.FieldAccessRefNode; import vadl.viam.graph.dependency.FieldRefNode; -import vadl.viam.graph.dependency.FloatBuiltInCall; import vadl.viam.graph.dependency.FoldNode; import vadl.viam.graph.dependency.ForIdxNode; import vadl.viam.graph.dependency.FuncCallNode; @@ -915,6 +913,11 @@ private ExpressionNode visitIdentifiable(Expr expr) { return new ConstantNode(value); } + if (computedTarget instanceof FloatTypeDefinition floatType) { + var format = (FloatFormat) viamLowering.fetch(floatType).orElseThrow(); + return new ConstantNode(new Constant.FloatType(format)); + } + // Enum field if (computedTarget instanceof EnumerationDefinition.Entry enumField) { // Inline the value of the enum @@ -1379,6 +1382,26 @@ public ExpressionNode visitStageCall(CallIndexExpr expr, StageDefinition stageDe return new ReadStageOutputNode(output); } + private Constant visitConstArg(Expr expr) { + Node origin = null; + if (expr instanceof Identifier identifier) { + origin = requireNonNull(identifier.target()); + } else if (expr instanceof IdentifierPath path) { + origin = requireNonNull(path.target()); + } + + // TODO: the constant evaluator can only evaluate integers currently. Once other stuff like + // strings and float-types are supported, we do not need this function anymore and can + // directly call the constant evaluator. + // !!! There is a similar method in TypeChecker + if (origin instanceof FloatTypeDefinition floatType) { + var format = (FloatFormat) viamLowering.fetch(floatType).orElseThrow(); + return new Constant.FloatType(format); + } + + return constantEvaluator.eval(expr).toViamConstant(); + } + @Override public ExpressionNode visit(CallIndexExpr expr) { @@ -1388,21 +1411,15 @@ public ExpressionNode visit(CallIndexExpr expr) { return visitStageCall(expr, stageDefinition); } + var symbolArgs = expr.symbolArgs(); + + var constArgs = symbolArgs != null + ? symbolArgs.stream().map(this::visitConstArg).toList() + : List.of(); + var argGroups = expr.args(); final var args = new NodeList(AstUtils.argumentCount(argGroups)); - final var floatTypeArgs = new ArrayList(); - AstUtils.forEachArgument(argGroups, arg -> { - var target = switch (arg) { - case Identifier identifier -> identifier.target(); - case IdentifierPath path -> path.target(); - default -> null; - }; - if (target instanceof FloatTypeDefinition floatTypeDef) { - floatTypeArgs.add((FloatFormat) viamLowering.fetch(floatTypeDef).orElseThrow()); - } else { - args.add(this.fetch(arg)); - } - }); + AstUtils.forEachArgument(argGroups, arg -> args.add(this.fetch(arg))); var typeBeforeSlice = getViamType(expr.typeBeforeSlice()); ExpressionNode exprBeforeSlice; @@ -1410,14 +1427,9 @@ public ExpressionNode visit(CallIndexExpr expr) { // Builtin Call if (expr.computedBuiltIn != null) { if (BuiltInTable.ASM_PARSER_BUILT_INS.contains(expr.computedBuiltIn)) { - exprBeforeSlice = new AsmBuiltInCall(expr.computedBuiltIn, args, - typeBeforeSlice); - } else if (BuiltInTable.FLOAT_BUILT_INS.contains(expr.computedBuiltIn)) { - exprBeforeSlice = new FloatBuiltInCall(expr.computedBuiltIn, args, - floatTypeArgs, typeBeforeSlice); + exprBeforeSlice = new AsmBuiltInCall(expr.computedBuiltIn, args, typeBeforeSlice); } else { - exprBeforeSlice = new BuiltInCall(expr.computedBuiltIn, args, - typeBeforeSlice); + exprBeforeSlice = new BuiltInCall(expr.computedBuiltIn, constArgs, args, typeBeforeSlice); } } else { exprBeforeSlice = switch (expr.computedTarget()) { @@ -1450,9 +1462,8 @@ yield readRegisterTensorDirect(((Counter) lowered).registerTensor(), aliasArgs, } case MemoryDefinition memDef -> { - var sizeExpr = expr.target.size(); - var words = sizeExpr != null - ? constantEvaluator.eval(sizeExpr).value().intValueExact() + var words = symbolArgs != null + ? constantEvaluator.eval(symbolArgs.getFirst()).value().intValueExact() : 1; yield new ReadMemNode((Memory) viamLowering.fetch(memDef).orElseThrow(), words, args.getFirst(), typeBeforeSlice.asDataType()); @@ -1763,9 +1774,9 @@ public SubgraphContext visit(AssignmentStatement statement) { } }); - var sizeExpr = callTarget.target.size(); - callSize = sizeExpr != null - ? constantEvaluator.eval(sizeExpr).value().intValueExact() + var symbolArgs = callTarget.target.symbolArgs(); + callSize = symbolArgs != null + ? constantEvaluator.eval(symbolArgs.getFirst()).value().intValueExact() : null; } else if (statement.target instanceof Identifier identTarget) { targetDef = (vadl.ast.nodes.Definition) requireNonNull(identTarget.target()); diff --git a/vadl-frontend/main/vadl/ast/ConstantEvaluator.java b/vadl-frontend/main/vadl/ast/ConstantEvaluator.java index 247320919..e5543722b 100644 --- a/vadl-frontend/main/vadl/ast/ConstantEvaluator.java +++ b/vadl-frontend/main/vadl/ast/ConstantEvaluator.java @@ -164,10 +164,18 @@ public ConstantValue evalBuiltin(BuiltInTable.BuiltIn builtin, List (Constant) c.toViamConstant()).toList()) + .compute(List.of(), args.stream().map(c -> (Constant) c.toViamConstant()).toList()) .orElseThrow(() -> new EvaluationError( "Built-in function `%s` cannot be constant evaluated (yet).".formatted( builtin.name()), diff --git a/vadl-frontend/main/vadl/ast/MacroExpander.java b/vadl-frontend/main/vadl/ast/MacroExpander.java index c1cd3a7eb..614583c3a 100644 --- a/vadl-frontend/main/vadl/ast/MacroExpander.java +++ b/vadl-frontend/main/vadl/ast/MacroExpander.java @@ -523,7 +523,12 @@ public Expr visit(CastExpr expr) { @Override public Expr visit(SymbolExpr expr) { - return new SymbolExpr(expandExpr(expr.path), expandExpr(expr.size), copyLoc(expr.location)); + var symbolArgs = new ArrayList(expr.symbolArgs.size()); + for (var i = 0; i < expr.symbolArgs.size(); i++) { + var symbolArg = expr.symbolArgs.get(i); + symbolArgs.add(expandExpr(symbolArg)); + } + return new SymbolExpr(expandExpr(expr.path), symbolArgs, copyLoc(expr.location)); } @Override diff --git a/vadl-frontend/main/vadl/ast/TypeChecker.java b/vadl-frontend/main/vadl/ast/TypeChecker.java index 3cfb9fb62..e7f7c52dc 100644 --- a/vadl-frontend/main/vadl/ast/TypeChecker.java +++ b/vadl-frontend/main/vadl/ast/TypeChecker.java @@ -177,7 +177,6 @@ import vadl.types.DataType; import vadl.types.FetchResultType; import vadl.types.FloatStatusType; -import vadl.types.FloatType; import vadl.types.GroupType; import vadl.types.InstructionType; import vadl.types.MicroArchitectureType; @@ -939,21 +938,28 @@ List applyCastToArgs(List args) { /// A tiny custom cache for built-in function type checking. private static class BuiltInCheckCache { - private Map, BuiltInCheckResult>> store = - new HashMap<>(); + private Map, Map, BuiltInCheckResult>>> + store = new HashMap<>(); @Nullable - private BuiltInCheckResult get(BuiltInTable.BuiltIn builtIn, List argTypes) { + private BuiltInCheckResult get(BuiltInTable.BuiltIn builtIn, List constArgs, + List argTypes) { var inner = store.get(builtIn); if (inner == null) { return null; } - return inner.get(argTypes); + var innerInner = inner.get(constArgs); + if (innerInner == null) { + return null; + } + return innerInner.get(argTypes); } - private void put(BuiltInTable.BuiltIn builtIn, List argTypes, BuiltInCheckResult result) { + private void put(BuiltInTable.BuiltIn builtIn, List constArgs, List argTypes, + BuiltInCheckResult result) { var inner = store.computeIfAbsent(builtIn, k -> new HashMap<>()); - inner.put(argTypes, result); + var innerInner = inner.computeIfAbsent(constArgs, k -> new HashMap<>()); + innerInner.put(argTypes, result); } } @@ -963,18 +969,19 @@ private void put(BuiltInTable.BuiltIn builtIn, List argTypes, BuiltInCheck /// Check if the built-in function call, but doesn't care which kind of expression it arises from /// binary expressions, unary expressions or direct calls. /// The passed arguments have to be already checked! - private BuiltInCheckResult checkBuiltin(BuiltInTable.BuiltIn builtIn, List args, - WithLocation location) { + private BuiltInCheckResult checkBuiltin(BuiltInTable.BuiltIn builtIn, List constArgs, + List args, WithLocation location) { List argTypes = new ArrayList<>(args.size()); for (int i = 0; i < args.size(); i++) { argTypes.add(args.get(i).type()); } - var cached = builtInCheckCache.get(builtIn, argTypes); + var constArgValues = constArgs.stream().map(this::evalConstArg).toList(); + var cached = builtInCheckCache.get(builtIn, constArgValues, argTypes); if (cached != null) { return cached; } - var result = unCachedCheckBuiltin(builtIn, args, location); + var result = unCachedCheckBuiltin(builtIn, constArgValues, args, location); // We cannot cache if the result is a constant but not all input types were also constant. // This is quite rare but here we simply cannot determine the result type simply based on the @@ -983,13 +990,14 @@ private BuiltInCheckResult checkBuiltin(BuiltInTable.BuiltIn builtIn, List return result; } - builtInCheckCache.put(builtIn, argTypes, result); + builtInCheckCache.put(builtIn, constArgValues, argTypes, result); return result; } - private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, List args, + private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, + List constArgs, List args, WithLocation location) { - int minArgCount = builtIn.argTypeClasses().size() + builtIn.signature().floatTypeArgCount(); + int minArgCount = builtIn.argTypeClasses().size(); if (!(args.size() == minArgCount || (builtIn.signature().hasVarArgs() && args.size() >= minArgCount))) { throw addErrorAndStopChecking( @@ -1233,14 +1241,11 @@ private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, Li // Now revert to the generic handling of functions. } - var ftArgCnt = builtIn.signature().floatTypeArgCount(); - var normalArgs = args.stream().skip(ftArgCnt).toList(); - - var argTypes = normalArgs.stream().map(Expr::type).toList(); + var argTypes = args.stream().map(Expr::type).toList(); var areAllConst = argTypes.stream().allMatch(ConstantType.class::isInstance); if (areAllConst) { var type = constantEvaluator - .evalBuiltin(builtIn, normalArgs.stream().map(constantEvaluator::eval).toList(), location) + .evalBuiltin(builtIn, args.stream().map(constantEvaluator::eval).toList(), location) .type(); return new BuiltInCheckResult(null, type); } @@ -1256,36 +1261,66 @@ private BuiltInCheckResult unCachedCheckBuiltin(BuiltInTable.BuiltIn builtIn, Li // Inject implicit casts for constant types // NOTE: There might be functions that operate on bit patterns where this implicit cast might // not be intended and should be disallowed. - normalArgs = Streams.zip(normalArgs.stream(), declaredTypes, - TypeChecker::wrapImplicitCastConstToTypeClass).toList(); + args = Streams.zip(args.stream(), declaredTypes, TypeChecker::wrapImplicitCastConstToTypeClass) + .toList(); var originalArgTypes = argTypes; - argTypes = normalArgs.stream().map(Expr::type).toList(); + argTypes = args.stream().map(Expr::type).toList(); - var ftArgs = args.stream().limit(ftArgCnt).toList(); - var ftTypes = ftArgs.stream().map(Expr::type).toList(); - var ftTypesInvalid = !ftTypes.stream().allMatch(FloatType.class::isInstance); - if (ftTypesInvalid || !builtIn.takes(argTypes)) { + if (!builtIn.takes(constArgs, argTypes)) { // FIXME: Further improve these error messages. var areSomeConst = originalArgTypes.stream().anyMatch(ConstantType.class::isInstance); - var calledTypes = Stream.concat(ftTypes.stream(), argTypes.stream()) - .map(Type::toString).collect(Collectors.joining(", ")); + var calledTypes = "(%s)".formatted( + String.join(", ", argTypes.stream().map(Type::toString).toList())); + if (!constArgs.isEmpty()) { + calledTypes = "<%s>%s".formatted( + String.join(", ", constArgs.stream().map(Constant::toString).toList()), + calledTypes + ); + } addErrorAndStopChecking( error("Type Mismatch", location) .locationDescription(location, "The builtin has the signature `%s` but got `%s`.", - builtIn.signature().nameWithFloatTypes(), calledTypes) + builtIn.signature(), calledTypes) .applyIf(areSomeConst, b -> b.locationHelp(location, "Try casting some of the constant arguments to explicit types.")) - .applyIf(ftTypesInvalid, b -> - b.help("The first %d arguments must be float-type.", ftArgCnt)) .build()); } - return new BuiltInCheckResult(argTypes, builtIn.returns(argTypes)); + return new BuiltInCheckResult(argTypes, builtIn.returns(constArgs, argTypes)); + } + + private Constant evalConstArg(Expr expr) { + Node origin = null; + String name = null; + if (expr instanceof Identifier identifier) { + origin = requireNonNull(identifier.target()); + name = identifier.name; + } else if (expr instanceof IdentifierPath path) { + origin = requireNonNull(path.target()); + name = path.toString(); + } + + // TODO: the constant evaluator can only evaluate integers currently. Once other stuff like + // strings and float-types are supported, we do not need this function anymore and can + // directly call the constant evaluator. + // !!! There is a similar method in BehaviorLowering + if (origin instanceof FloatTypeDefinition floatType) { + check(floatType); + if (floatType.size == null) { + addErrorAndStopChecking( + error("Missing float-type encoding size", floatType) + .help("Annotate with e.g. `[ IEEE : ]`") + .build() + ); + } + return new Constant.FloatType(requireNonNull(floatType.size), requireNonNull(name)); + } + + return constantEvaluator.eval(expr).toViamConstant(); } @Override public Void visit(FloatTypeDefinition definition) { - // Nothing to do return null; } @@ -3476,7 +3511,7 @@ public Void visit(BinaryExpr expr) { // operator and not in the builtin function we want to call. checkResult = checkLogicalBuiltIn(expr.left, expr.right, expr); } else { - checkResult = checkBuiltin(builtin, List.of(expr.left, expr.right), expr); + checkResult = checkBuiltin(builtin, List.of(), List.of(expr.left, expr.right), expr); } if (checkResult.castedArgTypes != null) { @@ -3863,7 +3898,7 @@ public Void visit(UnaryExpr expr) { }; expr.computedTarget = builtin; - var result = checkBuiltin(builtin, List.of(expr.operand), expr); + var result = checkBuiltin(builtin, List.of(), List.of(expr.operand), expr); if (result.castedArgTypes != null) { expr.operand = result.applyCastToArgs(List.of(expr.operand)).get(0); } @@ -4341,6 +4376,9 @@ private void processCallOfBuiltIn(CallIndexExpr expr) { // Builtin function List args = !expr.argsIndices.isEmpty() ? expr.argsIndices.getFirst().values : new ArrayList<>(); + var symbolArgs = expr.symbolArgs(); + var constArgs = symbolArgs == null ? List.of() : symbolArgs; + checkExpressions(constArgs); var argTypes = checkExpressions(args); var builtin = AstUtils.getBuiltIn(expr.target.path().pathToString(), argTypes); @@ -4353,14 +4391,9 @@ private void processCallOfBuiltIn(CallIndexExpr expr) { expr.computedBuiltIn = builtin; - var checkResult = checkBuiltin(builtin, args, expr); + var checkResult = checkBuiltin(builtin, constArgs, args, expr); if (checkResult.castedArgTypes != null) { - var ftArgCnt = builtin.signature().floatTypeArgCount(); - var newArgs = Stream.concat( - args.stream().limit(ftArgCnt), - checkResult.applyCastToArgs(args.stream().skip(ftArgCnt).toList()).stream() - ).toList(); - expr.replaceArgsFor(0, newArgs); + expr.replaceArgsFor(0, checkResult.applyCastToArgs(args)); } expr.typeBeforeSlice = checkResult.returnType; expr.argsIndices.get(0).type = checkResult.returnType; @@ -4492,8 +4525,18 @@ private void processCallOfTarget(CallIndexExpr expr, Node callTarget) { expr.typeBeforeSlice = typedNode.type(); } - var targetSizeExpr = expr.target.size(); - if (targetSizeExpr != null) { + var targetSymbolArgs = expr.target.symbolArgs(); + if (targetSymbolArgs != null) { + if (targetSymbolArgs.size() != 1) { + var err = error("Invalid scaling arguments", expr); + if (!targetSymbolArgs.isEmpty()) { + var loc = targetSymbolArgs.stream().map(WithLocation::location) + .reduce(SourceLocation::join).get(); + err = err.locationDescription(loc, "Expected exactly one scaling argument."); + } + throw addErrorAndStopChecking(err.build()); + } + var targetSizeExpr = targetSymbolArgs.getFirst(); if (!(expr.typeBeforeSlice() instanceof BitsType exprType)) { throw addErrorAndStopChecking(error("Invalid scaling type", targetSizeExpr) .locationDescription(targetSizeExpr, "Result type `%s` cannot be scaled.", diff --git a/vadl-frontend/main/vadl/ast/Ungrouper.java b/vadl-frontend/main/vadl/ast/Ungrouper.java index 1e8623655..03315d767 100644 --- a/vadl-frontend/main/vadl/ast/Ungrouper.java +++ b/vadl-frontend/main/vadl/ast/Ungrouper.java @@ -285,7 +285,7 @@ public Expr visit(CastExpr expr) { @Override public Expr visit(SymbolExpr expr) { - expr.size = expr.size.accept(this); + expr.symbolArgs.replaceAll(e -> e.accept(this)); return expr; } diff --git a/vadl-frontend/main/vadl/ast/nodes/CallIndexExpr.java b/vadl-frontend/main/vadl/ast/nodes/CallIndexExpr.java index 70615e3ac..3604a0a34 100644 --- a/vadl-frontend/main/vadl/ast/nodes/CallIndexExpr.java +++ b/vadl-frontend/main/vadl/ast/nodes/CallIndexExpr.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Objects; import java.util.function.Consumer; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; import vadl.ast.TensorType; import vadl.types.BuiltInTable; @@ -193,8 +194,9 @@ public IsId path() { } @Override - public @Nullable Expr size() { - return target.size(); + @Nullable + public List symbolArgs() { + return target.symbolArgs(); } @Override diff --git a/vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java b/vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java index c433d355e..dd331396b 100644 --- a/vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java +++ b/vadl-frontend/main/vadl/ast/nodes/FloatTypeDefinition.java @@ -17,6 +17,7 @@ package vadl.ast.nodes; import java.util.Objects; +import javax.annotation.Nullable; import vadl.types.Type; import vadl.utils.SourceLocation; @@ -24,6 +25,9 @@ public class FloatTypeDefinition extends Definition implements IdentifiableNode, TypedNode { public IdentifierOrPlaceholder identifier; + @Nullable + public Integer size; + public SourceLocation loc; public FloatTypeDefinition(IdentifierOrPlaceholder identifier, SourceLocation loc) { diff --git a/vadl-frontend/main/vadl/ast/nodes/IsCallExpr.java b/vadl-frontend/main/vadl/ast/nodes/IsCallExpr.java index 0afa56bde..10862b439 100644 --- a/vadl-frontend/main/vadl/ast/nodes/IsCallExpr.java +++ b/vadl-frontend/main/vadl/ast/nodes/IsCallExpr.java @@ -16,6 +16,7 @@ package vadl.ast.nodes; +import java.util.List; import javax.annotation.Nullable; import vadl.utils.WithLocation; @@ -24,7 +25,7 @@ public sealed interface IsCallExpr extends WithLocation permits CallIndexExpr, I public IsId path(); @Nullable - public Expr size(); + public List symbolArgs(); public void prettyPrint(int indent, StringBuilder builder); } diff --git a/vadl-frontend/main/vadl/ast/nodes/IsId.java b/vadl-frontend/main/vadl/ast/nodes/IsId.java index d8dabab2e..178292015 100644 --- a/vadl-frontend/main/vadl/ast/nodes/IsId.java +++ b/vadl-frontend/main/vadl/ast/nodes/IsId.java @@ -16,6 +16,7 @@ package vadl.ast.nodes; +import java.util.List; import javax.annotation.Nullable; @SuppressWarnings({"MissingJavadocType", "MissingJavadocMethod"}) @@ -28,7 +29,8 @@ public default IsId path() { } @Override - public default @Nullable Expr size() { + @Nullable + public default List symbolArgs() { return null; } diff --git a/vadl-frontend/main/vadl/ast/nodes/IsSymExpr.java b/vadl-frontend/main/vadl/ast/nodes/IsSymExpr.java index 9366995a8..73e6410e5 100644 --- a/vadl-frontend/main/vadl/ast/nodes/IsSymExpr.java +++ b/vadl-frontend/main/vadl/ast/nodes/IsSymExpr.java @@ -16,6 +16,7 @@ package vadl.ast.nodes; +import java.util.List; import javax.annotation.Nullable; @SuppressWarnings({"MissingJavadocType", "MissingJavadocMethod"}) @@ -25,5 +26,5 @@ public sealed interface IsSymExpr extends IsCallExpr permits SymbolExpr, IsId { @Override @Nullable - public Expr size(); + public List symbolArgs(); } diff --git a/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java b/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java index b62dbda77..7dba9410e 100644 --- a/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java +++ b/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java @@ -16,24 +16,29 @@ package vadl.ast.nodes; +import java.util.List; import java.util.Objects; import vadl.javaannotations.ast.Child; import vadl.utils.SourceLocation; /** - * A representation of terms of form {@code "MEM<9>"}. + * A representation of terms of form {@code MEM<9>} or {@code VADL::fcvts}. + * These terms always have at least one argument in the pointy brackets. */ @SuppressWarnings("MissingJavadocMethod") public final class SymbolExpr extends Expr implements IsSymExpr { @Child public IsId path; + /** + * The list of arguments in the pointy brackets. Always contains at least one element. + */ @Child - public Expr size; + public List symbolArgs; public SourceLocation location; - public SymbolExpr(IsId path, Expr size, SourceLocation location) { + public SymbolExpr(IsId path, List symbolArgs, SourceLocation location) { this.path = path; - this.size = size; + this.symbolArgs = symbolArgs; this.location = location; } @@ -43,8 +48,8 @@ public IsId path() { } @Override - public Expr size() { - return size; + public List symbolArgs() { + return symbolArgs; } @Override @@ -60,11 +65,16 @@ public SyntaxType syntaxType() { @Override public void prettyPrintExpr(int indent, StringBuilder builder, Precedence parentPrec) { path.prettyPrint(indent, builder); - var prefix = size instanceof BinaryExpr ? "<( " : "< "; - var suffix = size instanceof BinaryExpr ? " )>" : " >"; - builder.append(prefix); - size.prettyPrintExpr(indent, builder, Precedence.NoPrecedence); - builder.append(suffix); + builder.append("<"); + boolean first = true; + for (var arg : symbolArgs) { + if (!first) { + builder.append(", "); + } + arg.prettyPrintExpr(0, builder, Precedence.NoPrecedence); + first = false; + } + builder.append(">"); } @Override @@ -83,13 +93,13 @@ public boolean equals(Object o) { } SymbolExpr that = (SymbolExpr) o; - return path.equals(that.path) && Objects.equals(size, that.size); + return path.equals(that.path) && symbolArgs.equals(that.symbolArgs); } @Override public int hashCode() { int result = path.hashCode(); - result = 31 * result + Objects.hashCode(size); + result = 31 * result + Objects.hashCode(symbolArgs); return result; } } diff --git a/vadl-frontend/main/vadl/ast/nodes/TypeLiteral.java b/vadl-frontend/main/vadl/ast/nodes/TypeLiteral.java index 2c51afa4f..a208ad9d3 100644 --- a/vadl-frontend/main/vadl/ast/nodes/TypeLiteral.java +++ b/vadl-frontend/main/vadl/ast/nodes/TypeLiteral.java @@ -49,8 +49,8 @@ public TypeLiteral(IsId baseType, List sizeIndices, SourceLocation loc) { public TypeLiteral(IsSymExpr symExpr) { this.baseType = symExpr.path(); - var size = symExpr.size(); - this.sizeIndices = size == null ? List.of() : List.of(size); + var symbolArgs = symExpr.symbolArgs(); + this.sizeIndices = symbolArgs == null ? List.of() : List.of(symbolArgs.getFirst()); this.loc = symExpr.location(); } diff --git a/vadl-frontend/main/vadl/ast/vadl.ATG b/vadl-frontend/main/vadl/ast/vadl.ATG index 2b54344e6..aed18cf2b 100644 --- a/vadl-frontend/main/vadl/ast/vadl.ATG +++ b/vadl-frontend/main/vadl/ast/vadl.ATG @@ -1800,16 +1800,23 @@ A micro architecture definition (#microArchitectureDefinition) is shown in line // Symbol expressions of form "a::b<3>". // Due to the "<"-ambiguity with the less-than operator, this rule can also return a BinaryExpr. // Use the "allowLtOp" parameter to disallow this behavior, e.g. in type literals. - symbolOrBinaryExpression (. expr = DUMMY_EXPR; .) + symbolOrBinaryExpression (. expr = DUMMY_EXPR; boolean multipleTerms = false; .) = IF (isIdentifierToken(la) || isMacroReplacementOfType(this, BasicSyntaxType.ID)) identifierPath [ IF (la.kind == _SYM_LT) SYM_LT (. var lessLoc = lastTokenLoc(); .) - term + term (. var values = new ArrayList(); values.add(term); .) + // problem: here is ambiguity. E.g.: + // id(id, id) + // ^^^^ is this "id < id" and then "," or is it "id" and then ","? + { + SYM_COMMA + term (. values.add(nextTerm); multipleTerms = true; .) + } [ - IF (!allowLtOp || la.kind == _SYM_GT) - SYM_GT (. expr = new SymbolExpr(path, term, path.location().join(lastTokenLoc())); .) + IF (!allowLtOp || multipleTerms || la.kind == _SYM_GT) + SYM_GT (. expr = new SymbolExpr(path, values, path.location().join(lastTokenLoc())); .) ] (. if (expr.equals(DUMMY_EXPR)) expr = new BinaryExpr((Expr) path, new BinOp(Operator.Less, lessLoc), term); .) ] (. if (expr.equals(DUMMY_EXPR)) expr = (Expr) path; .) | macroReplacement (. expr = castExpr(this, node); .) diff --git a/vadl-test/resources/cosim_configs/riscv_config.toml b/vadl-test/resources/cosim_configs/riscv_config.toml index de66bfc63..ca3593dee 100644 --- a/vadl-test/resources/cosim_configs/riscv_config.toml +++ b/vadl-test/resources/cosim_configs/riscv_config.toml @@ -40,7 +40,7 @@ ignore_registers = [] name = "VADL" # The executable of the ISS -exec = "/opt/qemu/bin/qemu-system-rv64imf" +exec = "/opt/qemu/bin/qemu-system-rv64id" # The path to the compiled file to use for testing test_exec="./test-execs/test_vadl" @@ -73,7 +73,7 @@ endian = "little" # NOTE: This option is must be set per client to be able to account for different setup-codes per ISS # Skips the first n instructions -skip_n_instructions = 0 +skip_n_instructions = 9 # Settings for running the qemu-clients with gdb-debugging enabled. # Use these settings when possible instead of configuring the flags in the additional_args array since otherwise the runner will mark the client as finished because it did not respond in time. @@ -120,6 +120,75 @@ remote_target = "/tmp/gdb-cosim-VADL" [qemu.gdb_reg_map] pc = "pc" +x0 = "zero" +x1 = "ra" +x2 = "sp" +x3 = "gp" +x4 = "tp" +x5 = "t0" +x6 = "t1" +x7 = "t2" +x8 = "fp" +x9 = "s1" +x10 = "a0" +x11 = "a1" +x12 = "a2" +x13 = "a3" +x14 = "a4" +x15 = "a5" +x16 = "a6" +x17 = "a7" +x18 = "s2" +x19 = "s3" +x20 = "s4" +x21 = "s5" +x22 = "s6" +x23 = "s7" +x24 = "s8" +x25 = "s9" +x26 = "s10" +x27 = "s11" +x28 = "t3" +x29 = "t4" +x30 = "t5" +x31 = "t6" + +f0 = "ft0" +f1 = "ft1" +f2 = "ft2" +f3 = "ft3" +f4 = "ft4" +f5 = "ft5" +f6 = "ft6" +f7 = "ft7" +f8 = "fs0" +f9 = "fs1" +f10 = "fa0" +f11 = "fa1" +f12 = "fa2" +f13 = "fa3" +f14 = "fa4" +f15 = "fa5" +f16 = "fa6" +f17 = "fa7" +f18 = "fs2" +f19 = "fs3" +f20 = "fs4" +f21 = "fs5" +f22 = "fs6" +f23 = "fs7" +f24 = "fs8" +f25 = "fs9" +f26 = "fs10" +f27 = "fs11" +f28 = "ft8" +f29 = "ft9" +f30 = "ft10" +f31 = "ft11" + +# TODO: add remaining csr registers +fcsr = "fcsr" + # Defines the test-source and how to test [testing] diff --git a/vadl/main/resources/templates/iss/target/gen-arch/cpu.c b/vadl/main/resources/templates/iss/target/gen-arch/cpu.c index d5ab192a3..4a4b59cf2 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/cpu.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/cpu.c @@ -103,10 +103,6 @@ static void [(${gen_arch_lower})]_cpu_reset_hold(Object *obj, ResetType type) [# th:each="access : ${base_clear_cpu_accessors}"] [(${access.name})](env);[/] - // TODO: this disables nan-propagation. but this should be specified per-instruction - [# th:each="fmt : ${float_formats}"] - set_default_nan_mode(1, &env->fp_status_[(${fmt.name})]);[/] - [(${reset})] } diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index 9971f05d8..b4d42cd24 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -45,6 +45,8 @@ void prep_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.sticky_fe_flags}"] flags &= ~((1 - ((env->[(${reg.name_lower})] >> [(${flag.idx})]) & 1)) << [(${flag.flag_idx})]);[/][/] set_float_exception_flags(flags, s); + // TODO: this disables nan-propagation. this will be configurable via the vadl spec at some point + set_default_nan_mode(1, s); } void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status *s) { @@ -57,145 +59,93 @@ void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status env->[(${reg.name_lower})] &= ~((1 - (flags >> [(${flag.flag_idx})] & 1)) << [(${flag.idx})]);[/][/] } -#define FLOAT_FN_IEEE_FE_HELPER(S) \ - typedef uint##S##_t (*f##S##_fn_1)(uint##S##_t rs1, float_status *s); \ - typedef uint##S##_t (*f##S##_fn_2)(uint##S##_t rs1, uint##S##_t rs2, float_status *s); \ - typedef uint##S##_t (*f##S##_fn_3)(uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3, \ - int flags, float_status *s); \ - uint##S##_t f##S##_fn_1_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ - f##S##_fn_1 fn, float_status *s, \ - uint##S##_t rs1) { \ - prep_float_status_fe_flags(env, s); \ - uint##S##_t result = fn(rs1, s); \ - set_float_status_fe_flags(env, s); \ - return result; \ - } \ - uint##S##_t f##S##_fn_2_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ - f##S##_fn_2 fn, float_status *s, \ - uint##S##_t rs1, uint##S##_t rs2) { \ - prep_float_status_fe_flags(env, s); \ - uint##S##_t result = fn(rs1, rs2, s); \ - set_float_status_fe_flags(env, s); \ - return result; \ - } \ - uint##S##_t f##S##_fn_3_with_fe_flags(CPU[(${gen_arch_upper})]State *env, \ - f##S##_fn_3 fn, float_status *s, int flags, \ - uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3) { \ - prep_float_status_fe_flags(env, s); \ - uint##S##_t result = fn(rs1, rs2, rs3, flags, s); \ - set_float_status_fe_flags(env, s); \ - return result; \ - } +#define FLOAT_HELPER_BODY(RET_TY, CALL, FMT) \ + float_status *s = &env->fp_status_##FMT; \ + prep_float_status_fe_flags(env, s); \ + RET_TY result = CALL; \ + set_float_status_fe_flags(env, s); \ + return result; #define FLOAT_HELPER_1(S, FMT, NAME, QEMU_FUN) \ - uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1) { \ - return f64_fn_1_with_fe_flags(env, float##S##_##QEMU_FUN, \ - &env->fp_status_##FMT, rs1); \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, s), FMT) \ } #define FLOAT_HELPER_2(S, FMT, NAME, QEMU_FUN) \ - uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1, uint##S##_t rs2) { \ - return f64_fn_2_with_fe_flags(env, float##S##_##QEMU_FUN, \ - &env->fp_status_##FMT, rs1, rs2); \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2) { \ + FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, rs2, s), FMT) \ } #define FLOAT_HELPER_3(S, FMT, NAME, QEMU_FUN, FLAGS) \ - uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3) { \ - return f64_fn_3_with_fe_flags(env, float##S##_##QEMU_FUN, \ - &env->fp_status_##FMT, FLAGS, rs1, rs2, rs3); \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3) { \ + FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, rs2, rs3, FLAGS, s), FMT) \ } -#define FLOAT_HELPER_F2I(S, FMT, INT_FMT, NAME) \ - INT_FMT##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1) { \ - return f64_fn_1_with_fe_flags(env, float##S##_to_##INT_FMT, \ - &env->fp_status_##FMT, rs1); \ +#define FLOAT_HELPER_F2I(S, FMT, INT_S, INT_FMT, NAME) \ + uint##INT_S##_t helper_##FMT##_##INT_S##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + FLOAT_HELPER_BODY(uint##INT_S##_t, float##S##_to_##INT_FMT(rs1, s), FMT) \ } -#define FLOAT_HELPER_I2F(S, FMT, INT_FMT, NAME) \ - uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - INT_FMT##_t rs1) { \ - return f64_fn_1_with_fe_flags(env, INT_FMT##_to_##float##S, \ - &env->fp_status_##FMT, rs1); \ +#define FLOAT_HELPER_I2F(S, FMT, INT_S, INT_FMT, NAME) \ + uint##S##_t helper_##FMT##_##INT_S##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##INT_S##_t rs1) { \ + FLOAT_HELPER_BODY(uint##S##_t, INT_FMT##_to_##float##S(rs1, s), FMT) \ } -#define FLOAT_HELPER_F2F(S, S2, FMT, FMT2, NAME) \ - uint##S2##_t helper_##FMT##_##FMT2##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1) { \ - return f64_fn_1_with_fe_flags(env, float##S##_to_##float##S2, \ - &env->fp_status_##FMT, rs1); \ +#define FLOAT_HELPER_F2F(S, FMT, S2, FMT2, NAME) \ + uint##S2##_t helper_##FMT##_##FMT2##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + FLOAT_HELPER_BODY(uint##S2##_t, float##S##_to_##float##S2(rs1, s), FMT) \ } // TODO: optimize fe flags (maybe prep can be omitted; or flags set to avoid recomputation) #define FLOAT_HELPER_CMP(S, FMT, NAME, QEMU_FUN) \ - uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1, uint##S##_t rs2) { \ - return f64_fn_2_with_fe_flags(env, float##S##_##QEMU_FUN, \ - &env->fp_status_##FMT, rs1, rs2); \ + uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2) { \ + FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1, rs2, s), FMT) \ } -#define FLOAT_HELPER_CLASS(S, FMT, NAME, QEMU_FUN) \ - uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1) { \ - return f64_fn_1_with_fe_flags(env, float##S##_##QEMU_FUN, \ - &env->fp_status_##FMT, rs1); \ +#define FLOAT_HELPER_CLASSS(S, FMT, NAME, QEMU_FUN) \ + uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1, s), FMT) \ } -// just use uint64_t for everything for now to keep things simple -// the actual helper call signatures do contain the right sizes (and all unsigned) -FLOAT_FN_IEEE_FE_HELPER(64) - -[# th:each="fmt : ${float_formats}"] -FLOAT_HELPER_1([(${fmt.bit_size})], [(${fmt.name})], fsqrt, sqrt) - -FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fadd, add) -FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fsub, sub) -FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fmul, mul) -FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fdiv, div) - -FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fmadd, muladd, 0) -FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fmsub, muladd, float_muladd_negate_c) -FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fnmadd, muladd, float_muladd_negate_c | float_muladd_negate_product) -FLOAT_HELPER_3([(${fmt.bit_size})], [(${fmt.name})], fnmsub, muladd, float_muladd_negate_product) - -FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fmin, minimum_number) -FLOAT_HELPER_2([(${fmt.bit_size})], [(${fmt.name})], fmax, maximum_number) +#define FLOAT_HELPER_CLASS(S, FMT, NAME, QEMU_FUN) \ + uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1) { \ + FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1), FMT) \ + } // TODO: risc-v specifies eq as quiet. other ISAs might want to configure this -FLOAT_HELPER_CMP([(${fmt.bit_size})], [(${fmt.name})], flt, lt) -FLOAT_HELPER_CMP([(${fmt.bit_size})], [(${fmt.name})], fle, le) -FLOAT_HELPER_CMP([(${fmt.bit_size})], [(${fmt.name})], feq, eq_quiet) - -FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtfss) -FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtfsd) -FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint32, fcvtfus) -FLOAT_HELPER_F2I([(${fmt.bit_size})], [(${fmt.name})], uint64, fcvtfud) - -// FIXME: this is currently very complex and will change -[# th:if="${fmt.bit_size != 64}"] -FLOAT_HELPER_I2F(32, [(${fmt.name})], uint32, fcvtssf) -FLOAT_HELPER_I2F(32, [(${fmt.name})], uint64, fcvtsdf) -FLOAT_HELPER_I2F(32, [(${fmt.name})], uint32, fcvtusf) -FLOAT_HELPER_I2F(32, [(${fmt.name})], uint64, fcvtudf) -[/] -[# th:if="${fmt.bit_size != 32}"] -FLOAT_HELPER_I2F(64, [(${fmt.name})], uint32, fcvtssf2) -FLOAT_HELPER_I2F(64, [(${fmt.name})], uint64, fcvtsdf2) -FLOAT_HELPER_I2F(64, [(${fmt.name})], uint32, fcvtusf2) -FLOAT_HELPER_I2F(64, [(${fmt.name})], uint64, fcvtudf2) -[/] -[# th:each="fmt2 : ${float_formats}"][# th:if="${fmt.name != fmt2.name}"] -[# th:if="${fmt.bit_size != 32}"]FLOAT_HELPER_F2F([(${fmt.bit_size})], 32, [(${fmt.name})], [(${fmt2.name})], fcvtff)[/] -[# th:if="${fmt.bit_size != 64}"]FLOAT_HELPER_F2F([(${fmt.bit_size})], 64, [(${fmt.name})], [(${fmt2.name})], fcvtff2)[/] -[/][/] - -FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisinf, is_infinity) -FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fiszero, is_zero) -FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisneg, is_neg) -FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisdenorm, is_denormal) // TODO: less efficient than is_zero_or_denormal -FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fissnan, is_signaling_nan) -FLOAT_HELPER_CLASS([(${fmt.bit_size})], [(${fmt.name})], fisqnan, is_quiet_nan) + +[# th:each="c : ${float_builtins.fsqrt}"]FLOAT_HELPER_1([(${c[0].bit_size})], [(${c[0].name})], fsqrt, sqrt) +[/][# th:each="c : ${float_builtins.fadd}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fadd, add) +[/][# th:each="c : ${float_builtins.fsub}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fsub, sub) +[/][# th:each="c : ${float_builtins.fmul}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fmul, mul) +[/][# th:each="c : ${float_builtins.fdiv}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fdiv, div) +[/][# th:each="c : ${float_builtins.fmadd}"]FLOAT_HELPER_3([(${c[0].bit_size})], [(${c[0].name})], fmadd, muladd, 0) +[/][# th:each="c : ${float_builtins.fmsub}"]FLOAT_HELPER_3([(${c[0].bit_size})], [(${c[0].name})], fmsub, muladd, float_muladd_negate_c) +[/][# th:each="c : ${float_builtins.fnmadd}"]FLOAT_HELPER_3([(${c[0].bit_size})], [(${c[0].name})], fnmadd, muladd, float_muladd_negate_c | float_muladd_negate_product) +[/][# th:each="c : ${float_builtins.fnmsub}"]FLOAT_HELPER_3([(${c[0].bit_size})], [(${c[0].name})], fnmsub, muladd, float_muladd_negate_product) +[/][# th:each="c : ${float_builtins.fmin}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fmin, minimum_number) +[/][# th:each="c : ${float_builtins.fmax}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fmax, maximum_number) +[/][# th:each="c : ${float_builtins.flt}"]FLOAT_HELPER_CMP([(${c[0].bit_size})], [(${c[0].name})], flt, lt) +[/][# th:each="c : ${float_builtins.fle}"]FLOAT_HELPER_CMP([(${c[0].bit_size})], [(${c[0].name})], fle, le) +[/][# th:each="c : ${float_builtins.feq}"]FLOAT_HELPER_CMP([(${c[0].bit_size})], [(${c[0].name})], feq, eq_quiet) +[/][# th:each="c : ${float_builtins.fcvt}"]FLOAT_HELPER_F2F([(${c[0].bit_size})], [(${c[0].name})], [(${c[1].bit_size})], [(${c[1].name})], fcvt) +[/][# th:each="c : ${float_builtins.fcvtfs}"]FLOAT_HELPER_F2I([(${c[0].bit_size})], [(${c[0].name})], [(${c[1]})], int[(${c[1]})], fcvtfs) +[/][# th:each="c : ${float_builtins.fcvtfu}"]FLOAT_HELPER_F2I([(${c[0].bit_size})], [(${c[0].name})], [(${c[1]})], uint[(${c[1]})], fcvtfu) +[/][# th:each="c : ${float_builtins.fcvtsf}"]FLOAT_HELPER_I2F([(${c[0].bit_size})], [(${c[0].name})], [(${c[1]})], int[(${c[1]})], fcvtsf) +[/][# th:each="c : ${float_builtins.fcvtuf}"]FLOAT_HELPER_I2F([(${c[0].bit_size})], [(${c[0].name})], [(${c[1]})], uint[(${c[1]})], fcvtuf) +[/][# th:each="c : ${float_builtins.fisinf}"]FLOAT_HELPER_CLASS([(${c[0].bit_size})], [(${c[0].name})], fisinf, is_infinity) +[/][# th:each="c : ${float_builtins.fiszero}"]FLOAT_HELPER_CLASS([(${c[0].bit_size})], [(${c[0].name})], fiszero, is_zero) +[/][# th:each="c : ${float_builtins.fisneg}"]FLOAT_HELPER_CLASS([(${c[0].bit_size})], [(${c[0].name})], fisneg, is_neg) +[/][# th:each="c : ${float_builtins.fisdenorm}"]FLOAT_HELPER_CLASS([(${c[0].bit_size})], [(${c[0].name})], fisdenorm, is_denormal) // TODO: less efficient than is_zero_or_denormal +[/][# th:each="c : ${float_builtins.fissnan}"]FLOAT_HELPER_CLASSS([(${c[0].bit_size})], [(${c[0].name})], fissnan, is_signaling_nan) +[/][# th:each="c : ${float_builtins.fisqnan}"]FLOAT_HELPER_CLASSS([(${c[0].bit_size})], [(${c[0].name})], fisqnan, is_quiet_nan) [/] diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.h b/vadl/main/resources/templates/iss/target/gen-arch/helper.h index d1ecd51f8..e82dfa8b3 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.h @@ -13,50 +13,29 @@ DEF_HELPER_1(unsupported, noreturn, env) // float helpers -[# th:each="fmt : ${float_formats}"] -DEF_HELPER_FLAGS_2([(${fmt.name})]_fsqrt, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_fadd, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_fsub, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_fmul, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_fdiv, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_4([(${fmt.name})]_fmadd, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_4([(${fmt.name})]_fmsub, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_4([(${fmt.name})]_fnmadd, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_4([(${fmt.name})]_fnmsub, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_fmin, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_fmax, 0, i[(${fmt.bit_size})], env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) - -DEF_HELPER_FLAGS_3([(${fmt.name})]_flt, 0, i64, env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_fle, 0, i64, env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_3([(${fmt.name})]_feq, 0, i64, env, i[(${fmt.bit_size})], i[(${fmt.bit_size})]) - -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfss, 0, i32, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfsd, 0, i64, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfus, 0, i32, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtfud, 0, i64, env, i[(${fmt.bit_size})]) - -// FIXME: this is currently very complex and will change -[# th:if="${fmt.bit_size != 64}"] -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtssf, 0, i32, env, i32) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtsdf, 0, i32, env, i64) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtusf, 0, i32, env, i32) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtudf, 0, i32, env, i64) -[/] -[# th:if="${fmt.bit_size != 32}"] -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtssf2, 0, i64, env, i32) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtsdf2, 0, i64, env, i64) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtusf2, 0, i64, env, i32) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fcvtudf2, 0, i64, env, i64) -[/] -[# th:each="fmt2 : ${float_formats}"][# th:if="${fmt.name != fmt2.name}"] -[# th:if="${fmt.bit_size != 32}"]DEF_HELPER_FLAGS_2([(${fmt.name})]_[(${fmt2.name})]_fcvtff, 0, i32, env, i[(${fmt.bit_size})])[/] -[# th:if="${fmt.bit_size != 64}"]DEF_HELPER_FLAGS_2([(${fmt.name})]_[(${fmt2.name})]_fcvtff2, 0, i64, env, i[(${fmt.bit_size})])[/] -[/][/] - -DEF_HELPER_FLAGS_2([(${fmt.name})]_fisinf, 0, i64, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fiszero, 0, i64, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fisneg, 0, i64, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fisdenorm, 0, i64, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fissnan, 0, i64, env, i[(${fmt.bit_size})]) -DEF_HELPER_FLAGS_2([(${fmt.name})]_fisqnan, 0, i64, env, i[(${fmt.bit_size})]) -[/] +[# th:each="c : ${float_builtins.fsqrt}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fsqrt, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fadd}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fsub}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fmul}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fmul, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fdiv}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fdiv, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fmadd}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fmadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fmsub}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fmsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fnmadd}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fnmadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fnmsub}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fnmsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fmin}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fmin, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fmax}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fmax, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.flt}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_flt, 0, i64, env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fle}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fle, 0, i64, env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.feq}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_feq, 0, i64, env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fcvt}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1].name})]_fcvt, 0, i[(${c[1].bit_size})], env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fcvtfs}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtfs, 0, i[(${c[1]})], env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fcvtfu}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtfu, 0, i[(${c[1]})], env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fcvtsf}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtsf, 0, i[(${c[0].bit_size})], env, i[(${c[1]})]) +[/][# th:each="c : ${float_builtins.fcvtuf}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtuf, 0, i[(${c[0].bit_size})], env, i[(${c[1]})]) +[/][# th:each="c : ${float_builtins.fisinf}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fisinf, 0, i64, env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fiszero}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fiszero, 0, i64, env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fisneg}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fisneg, 0, i64, env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fisdenorm}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fisdenorm, 0, i64, env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fissnan}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fissnan, 0, i64, env, i[(${c[0].bit_size})]) +[/][# th:each="c : ${float_builtins.fisqnan}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fisqnan, 0, i64, env, i[(${c[0].bit_size})]) +[/] \ No newline at end of file diff --git a/vadl/main/vadl/dump/InfoUtils.java b/vadl/main/vadl/dump/InfoUtils.java index dc6610443..ceb8b6396 100644 --- a/vadl/main/vadl/dump/InfoUtils.java +++ b/vadl/main/vadl/dump/InfoUtils.java @@ -16,6 +16,7 @@ package vadl.dump; +import com.google.common.html.HtmlEscapers; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -230,7 +231,7 @@ public static Info.Expandable createCodeBlockExpandable(String title, """
    %s
                 
    - """.formatted(code) + """.formatted(HtmlEscapers.htmlEscaper().escape(code)) ); } diff --git a/vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java b/vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java new file mode 100644 index 000000000..8114fb1a8 --- /dev/null +++ b/vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText : © 2026 TU Wien +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package vadl.iss.passes.common; + +import static vadl.viam.ViamError.ensure; + +import java.io.IOException; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; +import javax.annotation.CheckForNull; +import vadl.configuration.IssConfiguration; +import vadl.iss.passes.AbstractIssPass; +import vadl.pass.PassName; +import vadl.pass.PassResults; +import vadl.types.BuiltInTable; +import vadl.utils.GraphUtils; +import vadl.utils.ViamUtils; +import vadl.viam.Constant; +import vadl.viam.Specification; +import vadl.viam.graph.dependency.BuiltInCall; +import vadl.viam.graph.dependency.ExpressionNode; + +/** + * Analyzes all VIAM behaviors and creates a list of all called float built-ins and + * for each a set of occurring configurations. The configuration is usually just the constant + * parameters the builtin has been called with (i.e. the parameters in the angle brackets {@code + * VADL::builtin<...>()}). + * + *

    For some builtins, additional information is added to the configuration (e.g. for + * {@link BuiltInTable#FCVTSF} and {@link BuiltInTable#FCVTUF}, the bit-size of the operand + * is added). + * + *

    The configuration can later be used to only emit helper functions that are actually required. + */ +public class IssFloatBuiltinCollectionPass extends AbstractIssPass { + + public IssFloatBuiltinCollectionPass(IssConfiguration configuration) { + super(configuration); + } + + @Override + public PassName getName() { + return PassName.of("Float Built-in Collection"); + } + + /** + * Output of the pass. + * {@code floatBuiltIns} saves all occurring float built-ins and for each a set of all unique + * constant parameters. + */ + public record Output(Map>> floatBuiltIns) { + } + + @CheckForNull + @Override + public Object execute(PassResults passResults, Specification viam) throws IOException { + IdentityHashMap>> floatBuiltIns = + new IdentityHashMap<>(); + + ViamUtils.findAllBehaviors(viam).forEach(g -> g.getNodes(BuiltInCall.class) + .filter(call -> BuiltInTable.FLOAT_BUILT_INS.contains(call.builtIn())) + .forEach(call -> handleFloatBuiltin(call, floatBuiltIns))); + + return new Output(floatBuiltIns); + } + + private void handleFloatBuiltin(BuiltInCall call, + IdentityHashMap>> + floatBuiltIns) { + // TODO: here we should check if the constant parameters form a supported float built-in config + // e.g. VADL::fcvt(...) is not valid, but this should be checked in the + // frontend. But we may not support 4-bit floats, so VADL::fadd(...) is valid, but + // not supported. + + // TODO: currently this treats every float-type declaration as a unique config. but if two use + // the same encoding (and relevant settings), we could convert the format to the encoding + // and avoid unnecessary helper declarations. + + var builtin = call.builtIn(); + var config = call.constArgs(); + if (builtin == BuiltInTable.FCVTSF || builtin == BuiltInTable.FCVTUF) { + // Node: For these builtins, the size of the int arguments is inferred by the type-checker. + // But the helper emitter needs to know the size, so it gets added to the config. + var size = ((ExpressionNode) call.inputs().findFirst().get()).type().asDataType().bitWidth(); + ensure(size == 32 || size == 64, "Expected VADL::fcvt[su]f input size to be either 32 or 64"); + // the old config may be an immutable list + config = Stream.concat(config.stream(), Stream.of(GraphUtils.intU(size, 32))).toList(); + call.setConstArgs(config); + } + floatBuiltIns.computeIfAbsent(builtin, b -> new HashSet<>()).add(config); + } + +} diff --git a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java index fcc0f496d..3a2117bb5 100644 --- a/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java +++ b/vadl/main/vadl/iss/passes/common/IssNormalizationPass.java @@ -881,72 +881,27 @@ public void handleFEQ(BuiltInCall input) { } @Override - public void handleFCVTFF(BuiltInCall input) { + public void handleFCVT(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } @Override - public void handleFCVTFF2(BuiltInCall input) { + public void handleFCVTFS(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } @Override - public void handleFCVTFSS(BuiltInCall input) { + public void handleFCVTFU(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } @Override - public void handleFCVTFSD(BuiltInCall input) { + public void handleFCVTSF(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } @Override - public void handleFCVTFUS(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTFUD(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTSSF(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTSDF(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTUSF(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTUDF(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTSSF2(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTSDF2(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTUSF2(BuiltInCall input) { - // do nothing (float ops are done by helper function, which handle everything) - } - - @Override - public void handleFCVTUDF2(BuiltInCall input) { + public void handleFCVTUF(BuiltInCall input) { // do nothing (float ops are done by helper function, which handle everything) } diff --git a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java index c0ca22e13..b09502af6 100644 --- a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java +++ b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java @@ -31,6 +31,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; import javax.annotation.Nullable; import vadl.configuration.IssConfiguration; import vadl.iss.passes.AbstractIssPass; @@ -112,7 +113,6 @@ import vadl.viam.graph.dependency.ConstantNode; import vadl.viam.graph.dependency.DependencyNode; import vadl.viam.graph.dependency.DynSliceNode; -import vadl.viam.graph.dependency.FloatBuiltInCall; import vadl.viam.graph.dependency.FoldNode; import vadl.viam.graph.dependency.ForIdxNode; import vadl.viam.graph.dependency.FuncCallNode; @@ -1279,20 +1279,11 @@ class BuiltInTcgLoweringExecutor { //// Float to Int Conversion //// - .set(BuiltInTable.FCVTFF, (ctx) -> floatHelperCall(ctx, 1, "fcvtff")) - .set(BuiltInTable.FCVTFF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtff2")) - .set(BuiltInTable.FCVTFSS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfss")) - .set(BuiltInTable.FCVTFSD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfsd")) - .set(BuiltInTable.FCVTFUS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfus")) - .set(BuiltInTable.FCVTFUD, (ctx) -> floatHelperCall(ctx, 1, "fcvtfud")) - .set(BuiltInTable.FCVTSSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtssf")) - .set(BuiltInTable.FCVTSDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtsdf")) - .set(BuiltInTable.FCVTUSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtusf")) - .set(BuiltInTable.FCVTUDF, (ctx) -> floatHelperCall(ctx, 1, "fcvtudf")) - .set(BuiltInTable.FCVTSSF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtssf2")) - .set(BuiltInTable.FCVTSDF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtsdf2")) - .set(BuiltInTable.FCVTUSF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtusf2")) - .set(BuiltInTable.FCVTUDF2, (ctx) -> floatHelperCall(ctx, 1, "fcvtudf2")) + .set(BuiltInTable.FCVT, (ctx) -> floatHelperCall(ctx, 1, "fcvt")) + .set(BuiltInTable.FCVTFS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfs")) + .set(BuiltInTable.FCVTFU, (ctx) -> floatHelperCall(ctx, 1, "fcvtfu")) + .set(BuiltInTable.FCVTSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtsf")) + .set(BuiltInTable.FCVTUF, (ctx) -> floatHelperCall(ctx, 1, "fcvtuf")) //// Float Classification //// @@ -1346,8 +1337,10 @@ private static BuiltInResult floatHelperCall(BuiltInTcgLoweringExecutor.Context String name) { return out(new TcgHelperCall( ctx.dest(), new NodeList<>(IntStream.range(0, argc).mapToObj(ctx::src).toList()), true, - ctx.floatFormats().stream().map(FloatFormat::nameLower).collect(Collectors.joining("_")) - + "_" + name + Stream.concat( + ctx.floatFormats().stream().map(FloatFormat::nameLower), + ctx.constIntArgs().stream().map(Object::toString) + ).collect(Collectors.joining("_")) + "_" + name )); } @@ -1387,9 +1380,23 @@ private TcgVRefNode src(int index) { * @return A list of the float formats. */ private List floatFormats() { - call.ensure(call instanceof FloatBuiltInCall, "Call is not float built-in"); - var floatCall = (FloatBuiltInCall) call; - return floatCall.formats(); + return call.constArgs().stream() + .filter(Constant.FloatType.class::isInstance) + .map(c -> requireNonNull((Constant.FloatType) c).format()) + .toList(); + } + + /** + * Retrieves the constant arguments that are integers. I.e. all {@link Constant.Value} + * passed via the angle brackets `VADL::builtin<...>()`. + * + * @return A list of the constant integer arguments. + */ + private List constIntArgs() { + return call.constArgs().stream() + .filter(Constant.Value.class::isInstance) + .map(c -> requireNonNull((Constant.Value) c).intValue()) + .toList(); } /** diff --git a/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java b/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java index 47a3fcc8c..0b3e6546e 100644 --- a/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java +++ b/vadl/main/vadl/iss/template/IssTemplateRenderingPass.java @@ -21,25 +21,28 @@ import static vadl.error.Diagnostic.error; import static vadl.iss.template.IssRenderUtils.mapRegTensors; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.apache.commons.io.FilenameUtils; import vadl.configuration.IssConfiguration; import vadl.cppCodeGen.formatting.CodeFormatter; import vadl.iss.IssUtils; import vadl.iss.codegen.QemuClangFormatter; +import vadl.iss.passes.common.IssFloatBuiltinCollectionPass; import vadl.iss.passes.extensions.ExceptionInfo; import vadl.iss.passes.extensions.MemoryRegionInfo; import vadl.iss.passes.extensions.RegInfo; import vadl.pass.PassName; import vadl.pass.PassResults; import vadl.template.AbstractTemplateRenderingPass; +import vadl.types.BuiltInTable; +import vadl.viam.Constant; import vadl.viam.Endianness; -import vadl.viam.FloatFormat; import vadl.viam.Memory; import vadl.viam.Specification; @@ -133,8 +136,11 @@ protected Map createVariables(PassResults passResults, vars.put("gen_machine_upper", configuration().machineName().toUpperCase()); vars.put("gen_machine_lower", configuration().machineName().toLowerCase()); vars.put("register_tensors", mapRegTensors(specification)); + vars.put("float_builtins", getFloatBuiltins(passResults.lastResultOf( + IssFloatBuiltinCollectionPass.class, + IssFloatBuiltinCollectionPass.Output.class + ))); vars.put("float_formats", getFloatFormats(specification)); - vars.put("float_ieee_sizes", getFloatIEEESizes(specification)); vars.put("pc_info", getPcInfo(specification)); vars.put("target_size", configuration().targetSize().width); vars.put("mem_regions", memRegions(specification)); @@ -153,6 +159,26 @@ private ExceptionInfo getExceptionInfo(Specification viam) { return viam.processor().get().isa().expectExtension(ExceptionInfo.class); } + private Map>> getFloatBuiltins( + IssFloatBuiltinCollectionPass.Output config) { + var conf = config.floatBuiltIns().entrySet().stream().collect(Collectors.toMap( + e -> builtInName(e.getKey()), + e -> getFloatBuiltinConfigs(e.getValue()) + )); + return conf; + } + + private List> getFloatBuiltinConfigs(Set> configs) { + return configs.stream().map(config -> config.stream().map(c -> switch (c) { + case Constant.FloatType ft -> Map.of( + "bit_size", ft.size(), + "name", requireNonNull(ft.format()).nameLower() + ); + case Constant.Value v -> Integer.toString(v.intValue()); + default -> throw new IllegalStateException(); + }).toList()).toList(); + } + private List> getFloatFormats(Specification viam) { return viam.isa().get().ownFloatFormats().stream().map(fmt -> Map.of( "name", fmt.nameLower(), @@ -160,13 +186,8 @@ private List> getFloatFormats(Specification viam) { )).toList(); } - private List getFloatIEEESizes(Specification viam) { - return viam.isa().get().ownFloatFormats().stream() - .map(fmt -> requireNonNull(fmt.encoding())) - .filter(e -> e.ieee).map(e -> e.size) - .distinct() - .map(size -> Integer.toString(size)) - .toList(); + private String builtInName(BuiltInTable.BuiltIn builtin) { + return builtin.name().substring(builtin.name().lastIndexOf(':') + 1); } private Map getPcInfo(Specification viam) { diff --git a/vadl/main/vadl/pass/order/IssPassOrder.java b/vadl/main/vadl/pass/order/IssPassOrder.java index 4ddf6298c..d847c2d1f 100644 --- a/vadl/main/vadl/pass/order/IssPassOrder.java +++ b/vadl/main/vadl/pass/order/IssPassOrder.java @@ -30,6 +30,7 @@ import vadl.iss.passes.common.IssCommonExprSavePass; import vadl.iss.passes.common.IssConfigurationPass; import vadl.iss.passes.common.IssExtractOptimizationPass; +import vadl.iss.passes.common.IssFloatBuiltinCollectionPass; import vadl.iss.passes.common.IssGdbInfoExtractionPass; import vadl.iss.passes.common.IssInfoRetrievalPass; import vadl.iss.passes.common.IssLoopUnrollPass; @@ -140,7 +141,8 @@ private static void addCommonPasses(PassOrder order, IssConfiguration config) { .add(new SideEffectSchedulingPass(config)) .add(new IssSafeResourceReadPass(config)) .add(new IssCommonExprSavePass(config)) - .add(new IssScheduleIndirectJumpsPass(config)); + .add(new IssScheduleIndirectJumpsPass(config)) + .add(new IssFloatBuiltinCollectionPass(config)); } private static void addScalarTcgPasses(PassOrder order, IssConfiguration config) { diff --git a/vadl/main/vadl/types/BuiltInTable.java b/vadl/main/vadl/types/BuiltInTable.java index 094259cd1..74077f952 100644 --- a/vadl/main/vadl/types/BuiltInTable.java +++ b/vadl/main/vadl/types/BuiltInTable.java @@ -16,6 +16,7 @@ package vadl.types; +import static java.util.Objects.requireNonNull; import static org.slf4j.LoggerFactory.getLogger; import static vadl.types.Type.bits; import static vadl.types.Type.constructDataType; @@ -1044,23 +1045,27 @@ public class BuiltInTable { ///// FLOAT ARITHMETIC ////// /** - * {@code function fsqrt( t : FloatType, a : Bits, rm : Bits<3> ) -> Bits } + * {@code function fsqrt< t : FloatType >( a : Bits, rm : Bits<3> ) -> Bits } */ public static final BuiltIn FSQRT = func("VADL::fsqrt", - Type.relation(List.of(BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFrm(1) - .returnsFirstBitWidth(BitsType.class) + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(1) + .returnsFirstFloatType() .build(); /** - * {@code function fadd( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + * {@code function fadd< t : FloatType >( a : Bits, b : Bits, rm : Bits<3> ) -> + * Bits } */ public static final BuiltIn FADD = func("VADL::fadd", - Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstTwoWithSameBitWidthsAndFrm(2) + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(2) .returnsFirstBitWidth(BitsType.class) + .returnsFirstFloatType() .build(); // TODO: I think we want a status variant for float built-ins, similar to other built-ins. @@ -1069,375 +1074,294 @@ public class BuiltInTable { // possible. So we'd need to store them in a reg or an env variable and load them later. // But we have to make sure that other float ops are not scheduled in between! /** - * {@code function fadds( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> - * ( Bits, FloatStatus ) } + * {@code function fadds< t : FloatType >( a : Bits, b : Bits, rm : Bits<3> ) -> + * ( Bits, FloatStatus ) } */ public static final BuiltIn FADDS = - func("VADL::fadds", Type.relation( - List.of(BitsType.class, BitsType.class, BitsType.class), 1, StructType.class)) - .takesFirstTwoWithSameBitWidthsAndFrm(2) - .returnsFirstBitWidthAndFloatStatus() + func("VADL::fadds", + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), StructType.class)) + .takesFloatArgsAndFrm(2) + // TODO: returns function + .returnsFirstFloatType() .build(); /** - * {@code function fsub( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + * {@code function fsub< t : FloatType >( a : Bits, b : Bits, rm : Bits<3> ) -> + * Bits } */ public static final BuiltIn FSUB = func("VADL::fsub", - Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstTwoWithSameBitWidthsAndFrm(2) - .returnsFirstBitWidth(BitsType.class) + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(2) + .returnsFirstFloatType() .build(); /** - * {@code function fmul( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + * {@code function fmul< t : FloatType >( a : Bits, b : Bits, rm : Bits<3> ) -> + * Bits } */ public static final BuiltIn FMUL = func("VADL::fmul", - Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstTwoWithSameBitWidthsAndFrm(2) - .returnsFirstBitWidth(BitsType.class) + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(2) + .returnsFirstFloatType() .build(); /** - * {@code function fdiv( t : FloatType, a : Bits, b : Bits, rm : Bits<3> ) -> Bits } + * {@code function fdiv< t : FloatType >( a : Bits, b : Bits, rm : Bits<3> ) -> + * Bits } */ public static final BuiltIn FDIV = func("VADL::fdiv", - Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstTwoWithSameBitWidthsAndFrm(2) - .returnsFirstBitWidth(BitsType.class) + Type.relation(List.of(BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(2) + .returnsFirstFloatType() .build(); /** - * {@code function fmadd( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) - * -> Bits } + * {@code function fmadd< t : FloatType >( a : Bits, b : Bits, c : Bits, + * rm : Bits<3> ) -> Bits } */ public static final BuiltIn FMADD = func("VADL::fmadd", Type.relation(List.of( - BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstThreeWithSameBitWidthsAndFrm(3) - .returnsFirstBitWidth(BitsType.class) + BitsType.class, BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(3) + .returnsFirstFloatType() .build(); /** - * {@code function fmsub( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) - * -> Bits } + * {@code function fmsub< t : FloatType >( a : Bits, b : Bits, c : Bits, + * rm : Bits<3> ) -> Bits } */ public static final BuiltIn FMSUB = func("VADL::fmsub", Type.relation(List.of( - BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstThreeWithSameBitWidthsAndFrm(3) - .returnsFirstBitWidth(BitsType.class) + BitsType.class, BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(3) + .returnsFirstFloatType() .build(); /** - * {@code function fnmadd( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) - * -> Bits } + * {@code function fnmadd< t : FloatType >( a : Bits, b : Bits, c : Bits, + * rm : Bits<3> ) -> Bits } */ public static final BuiltIn FNMADD = func("VADL::fnmadd", Type.relation(List.of( - BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstThreeWithSameBitWidthsAndFrm(3) - .returnsFirstBitWidth(BitsType.class) + BitsType.class, BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(3) + .returnsFirstFloatType() .build(); /** - * {@code function fnmsub( t : FloatType, a : Bits, b : Bits, c : Bits, rm : Bits<3> ) - * -> Bits } + * {@code function fnmsub< t : FloatType >( a : Bits, b : Bits, c : Bits, + * rm : Bits<3> ) -> Bits } */ public static final BuiltIn FNMSUB = func("VADL::fnmsub", Type.relation(List.of( - BitsType.class, BitsType.class, BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstThreeWithSameBitWidthsAndFrm(3) - .returnsFirstBitWidth(BitsType.class) + BitsType.class, BitsType.class, BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(3) + .returnsFirstFloatType() .build(); /** - * {@code function fmin( t : FloatType, a : Bits, b : Bits ) -> Bits } + * {@code function fmin< t : FloatType >( a : Bits, b : Bits ) -> Bits } */ public static final BuiltIn FMIN = func("VADL::fmin", - Type.relation(List.of(BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstTwoWithSameBitWidths() - .returnsFirstBitWidth(BitsType.class) + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgs(2) + .returnsFirstFloatType() .build(); /** - * {@code function fmax( t : FloatType, a : Bits, b : Bits ) -> Bits } + * {@code function fmax< t : FloatType >( a : Bits, b : Bits ) -> Bits } */ public static final BuiltIn FMAX = func("VADL::fmax", - Type.relation(List.of(BitsType.class, BitsType.class), 1, BitsType.class)) - .takesFirstTwoWithSameBitWidths() - .returnsFirstBitWidth(BitsType.class) + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) + .takesFloatArgs(2) + .returnsFirstFloatType() .build(); ///// FLOAT COMPARISON ////// /** - * {@code function flt( t : FloatType, a : Bits, b : Bits ) -> Bool } + * {@code function flt< t : FloatType >( a : Bits, b : Bits ) -> Bool } */ public static final BuiltIn FLT = func("VADL::flt", - Type.relation(List.of(BitsType.class, BitsType.class), 1, BoolType.class)) - .takesFirstTwoWithSameBitWidths() + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(2) .returns(Type.bool()) .build(); /** - * {@code function fle( t : FloatType, a : Bits, b : Bits ) -> Bool } + * {@code function fle< t : FloatType >( a : Bits, b : Bits ) -> Bool } */ public static final BuiltIn FLE = func("VADL::fle", - Type.relation(List.of(BitsType.class, BitsType.class), 1, BoolType.class)) - .takesFirstTwoWithSameBitWidths() + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(2) .returns(Type.bool()) .build(); /** - * {@code function feq( t : FloatType, a : Bits, b : Bits ) -> Bool } + * {@code function feq< t : FloatType >( a : Bits, b : Bits ) -> Bool } */ public static final BuiltIn FEQ = func("VADL::feq", - Type.relation(List.of(BitsType.class, BitsType.class), 1, BoolType.class)) - .takesFirstTwoWithSameBitWidths() + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(2) .returns(Type.bool()) .build(); - ///// FLOAT TO FLOAT CONVERSION ////// - - // FIXME: here the same problem as with int-to-float built-ins - - /** - * Float conversion from float of type t to float of type u. - * {@code function fcvtff( t : FloatType, u : FloatType, a : Bits, rm : Bits<3> ) - * -> Bits<32> } - */ - public static final BuiltIn FCVTFF = - func("VADL::fcvtff", - Type.relation(List.of(BitsType.class, BitsType.class), 2, BitsType.class)) - .takesFrm(1) - .returns(bits(32)) - .build(); - - /** - * Float conversion from float of type t to float of type u. - * {@code function fcvtff2( t : FloatType, u : FloatType, a : Bits, rm : Bits<3> ) - * -> Bits<64> } - */ - public static final BuiltIn FCVTFF2 = - func("VADL::fcvtff2", - Type.relation(List.of(BitsType.class, BitsType.class), 2, BitsType.class)) - .takesFrm(1) - .returns(bits(64)) - .build(); - - ///// FLOAT TO INT CONVERSION ////// - - /** - * Float conversion from float to signed single. - * {@code function fcvtfss( t : FloatType, a : Bits, rm : Bits<3> ) -> SInt<32> } - */ - public static final BuiltIn FCVTFSS = - func("VADL::fcvtfss", - Type.relation(List.of(BitsType.class, BitsType.class), 1, SIntType.class)) - .takesFrm(1) - .returns(signedInt(32)) - .build(); - - /** - * Float conversion from float to signed double. - * {@code function fcvtfsd( t : FloatType, a : Bits, rm : Bits<3> ) -> SInt<64> } - */ - public static final BuiltIn FCVTFSD = - func("VADL::fcvtfsd", - Type.relation(List.of(BitsType.class, BitsType.class), 1, SIntType.class)) - .takesFrm(1) - .returns(signedInt(64)) - .build(); - - /** - * Float conversion from float to unsigned single. - * {@code function fcvtfus( t : FloatType, a : Bits, rm : Bits<3> ) -> UInt<32> } - */ - public static final BuiltIn FCVTFUS = - func("VADL::fcvtfus", - Type.relation(List.of(BitsType.class, BitsType.class), 1, UIntType.class)) - .takesFrm(1) - .returns(signedInt(32)) - .build(); - - /** - * Float conversion from float to unsigned double. - * {@code function fcvtfud( t : FloatType, a : Bits, rm : Bits<3> ) -> UInt<64> } - */ - public static final BuiltIn FCVTFUD = - func("VADL::fcvtfud", - Type.relation(List.of(BitsType.class, BitsType.class), 1, UIntType.class)) - .takesFrm(1) - .returns(signedInt(64)) - .build(); - - ///// INT TO FLOAT CONVERSION ////// - - // FIXME: here there is a problem: how does the type system infer how large the returned - // float type is? For now, its hardcoded at 32 bits, but it should actually be determined - // by the passed float-type. Idea: can the type-checker handle this special case? - // We could use type parameters... - // -> for now the following 4 built-ins use hard-coded return-sizes - - /** - * Float conversion from signed single to float. - * {@code function fcvtssf( t : FloatType, a : SInt<32>, rm : Bits<3> ) -> Bits<32> } - */ - public static final BuiltIn FCVTSSF = - func("VADL::fcvtssf", - Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) - .takesFrm(1) - .returns(bits(32)) - .build(); + ///// FLOAT CONVERSION ////// - /** - * Float conversion from signed double to float. - * {@code function fcvtsdf( t : FloatType, a : SInt<64>, rm : Bits<3> ) -> Bits<32> } - */ - public static final BuiltIn FCVTSDF = - func("VADL::fcvtsdf", - Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) - .takesFrm(1) - .returns(bits(32)) - .build(); + // TODO: currently the system allows conversion between all float types. We need to restrict + // that to a set of supported conversions (currently ieee 32->64 and 64->32). Should this + // happen during type-checking or in the backend? Different backends might be at different + // stages of implementation and may not support everything other backends do... /** - * Float conversion from unsigned single to float. - * {@code function fcvtusf( t : FloatType, a : UInt<32>, rm : Bits<3> ) -> Bits<32> } + * Float to float conversion. + * {@code function fcvt< t : FloatType, u : FloatType >( a : Bits, rm : Bits<3> ) -> + * Bits } */ - public static final BuiltIn FCVTUSF = - func("VADL::fcvtusf", - Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) - .takesFrm(1) - .returns(bits(32)) + public static final BuiltIn FCVT = + func("VADL::fcvt", + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class, FloatType.class), BitsType.class)) + .takesFloatArgsAndFrm(1) + .returnsSecondFloatType() .build(); - /** - * Float conversion from unsigned double to float. - * {@code function fcvtudf( t : FloatType, a : UInt<64>, rm : Bits<3> ) -> Bits<32> } - */ - public static final BuiltIn FCVTUDF = - func("VADL::fcvtudf", - Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) - .takesFrm(1) - .returns(bits(32)) - .build(); + // TODO: Integer results have to be truncated by the IssNormalizationPass /** - * Float conversion from signed single to float. - * {@code function fcvtssf2( t : FloatType, a : SInt<32>, rm : Bits<3> ) -> Bits<64> } + * Float to signed int conversion. + * {@code function fcvtfs< t : FloatType, s : UInt >( a : Bits, rm : Bits<3> ) -> + * SInt } */ - public static final BuiltIn FCVTSSF2 = - func("VADL::fcvtssf2", - Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) - .takesFrm(1) - .returns(bits(64)) + public static final BuiltIn FCVTFS = + func("VADL::fcvtfs", + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class, UIntType.class), SIntType.class)) + .takesFloatArgsAndFrm(1) + .returnsFromSecondConstSize(SIntType.class) .build(); /** - * Float conversion from signed double to float. - * {@code function fcvtsdf2( t : FloatType, a : SInt<64>, rm : Bits<3> ) -> Bits<64> } + * Float to unsigned int conversion. + * {@code function fcvtfu< t : FloatType, s : UInt >( a : Bits, rm : Bits<3> ) -> + * UInt } */ - public static final BuiltIn FCVTSDF2 = - func("VADL::fcvtsdf2", - Type.relation(List.of(SIntType.class, BitsType.class), 1, BitsType.class)) - .takesFrm(1) - .returns(bits(64)) + public static final BuiltIn FCVTFU = + func("VADL::fcvtfu", + Type.relation(List.of(BitsType.class, BitsType.class), + List.of(FloatType.class, UIntType.class), UIntType.class)) + .takesFloatArgsAndFrm(1) + .returnsFromSecondConstSize(UIntType.class) .build(); /** - * Float conversion from unsigned single to float. - * {@code function fcvtusf2( t : FloatType, a : UInt<32>, rm : Bits<3> ) -> Bits<64> } + * Signed int to float conversion. + * {@code function fcvtsf< t : FloatType >( a : SInt, rm : Bits<3> ) -> Bits } */ - public static final BuiltIn FCVTUSF2 = - func("VADL::fcvtusf2", - Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) + public static final BuiltIn FCVTSF = + func("VADL::fcvtsf", + Type.relation(List.of(SIntType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) .takesFrm(1) - .returns(bits(64)) + .returnsFirstFloatType() .build(); /** - * Float conversion from unsigned double to float. - * {@code function fcvtudf2( t : FloatType, a : UInt<64>, rm : Bits<3> ) -> Bits<64> } + * Unsigned int to float conversion. + * {@code function fcvtuf< t : FloatType >( a : UInt, rm : Bits<3> ) -> Bits } */ - public static final BuiltIn FCVTUDF2 = - func("VADL::fcvtudf2", - Type.relation(List.of(UIntType.class, BitsType.class), 1, BitsType.class)) + public static final BuiltIn FCVTUF = + func("VADL::fcvtuf", + Type.relation(List.of(UIntType.class, BitsType.class), + List.of(FloatType.class), BitsType.class)) .takesFrm(1) - .returns(bits(64)) + .returnsFirstFloatType() .build(); ///// FLOAT CLASSIFICATION ////// /** - * {@code function fisinf( t : FloatType, a : Bits ) -> Bool } + * {@code function fisinf< t : FloatType >( a : Bits ) -> Bool } */ public static final BuiltIn FISINF = func("VADL::fisinf", - Type.relation(List.of(BitsType.class), 1, BoolType.class)) - .takesDefault() + Type.relation(List.of(BitsType.class), List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(1) .returns(Type.bool()) .build(); /** - * {@code function fiszero( t : FloatType, a : Bits ) -> Bool } + * {@code function fiszero< t : FloatType >( a : Bits ) -> Bool } */ public static final BuiltIn FISZERO = func("VADL::fiszero", - Type.relation(List.of(BitsType.class), 1, BoolType.class)) - .takesDefault() + Type.relation(List.of(BitsType.class), List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(1) .returns(Type.bool()) .build(); /** - * {@code function fisneg( t : FloatType, a : Bits ) -> Bool } + * {@code function fisneg< t : FloatType >( a : Bits ) -> Bool } */ public static final BuiltIn FISNEG = func("VADL::fisneg", - Type.relation(List.of(BitsType.class), 1, BoolType.class)) - .takesDefault() + Type.relation(List.of(BitsType.class), List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(1) .returns(Type.bool()) .build(); /** - * {@code function fisdenorm( t : FloatType, a : Bits ) -> Bool } + * {@code function fisdenorm< t : FloatType >( a : Bits ) -> Bool } */ public static final BuiltIn FISDENORM = func("VADL::fisdenorm", - Type.relation(List.of(BitsType.class), 1, BoolType.class)) - .takesDefault() + Type.relation(List.of(BitsType.class), List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(1) .returns(Type.bool()) .build(); /** - * {@code function fissnan( t : FloatType, a : Bits ) -> Bool } + * {@code function fissnan< t : FloatType >( a : Bits ) -> Bool } */ public static final BuiltIn FISSNAN = func("VADL::fissnan", - Type.relation(List.of(BitsType.class), 1, BoolType.class)) - .takesDefault() + Type.relation(List.of(BitsType.class), List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(1) .returns(Type.bool()) .build(); /** - * {@code function fisqnan( t : FloatType, a : Bits ) -> Bool } + * {@code function fisqnan< t : FloatType >( a : Bits ) -> Bool } */ public static final BuiltIn FISQNAN = func("VADL::fisqnan", - Type.relation(List.of(BitsType.class), 1, BoolType.class)) - .takesDefault() + Type.relation(List.of(BitsType.class), List.of(FloatType.class), BoolType.class)) + .takesFloatArgs(1) .returns(Type.bool()) .build(); @@ -1592,7 +1516,7 @@ public class BuiltInTable { */ public static final BuiltIn LA_ID_IN = func("LaIdIn", null, - Type.relation(List.of(UIntType.class, StringType.class), true, 0, BoolType.class)) + Type.relation(List.of(UIntType.class, StringType.class), true, BoolType.class)) .takesDefault() .noCompute() .returns(Type.bool()) @@ -1606,7 +1530,7 @@ public class BuiltInTable { */ public static final BuiltIn LA_KIND_IN = func("LaKindIn", null, - Type.relation(List.of(UIntType.class, StringType.class), true, 0, BoolType.class)) + Type.relation(List.of(UIntType.class, StringType.class), true, BoolType.class)) .takesDefault() .noCompute() .returns(Type.bool()) @@ -1833,20 +1757,11 @@ private static BuiltIn instr(String name) { ); public static final List FLOAT_CONVERSION_BUILT_INS = List.of( - FCVTFF, - FCVTFF2, - FCVTFSS, - FCVTFSD, - FCVTFUS, - FCVTFUD, - FCVTSSF, - FCVTSDF, - FCVTUSF, - FCVTUDF, - FCVTSSF2, - FCVTSDF2, - FCVTUSF2, - FCVTUDF2 + FCVT, + FCVTFS, + FCVTFU, + FCVTSF, + FCVTUF ); public static final List FLOAT_CLASSIFICATION_BUILT_INS = List.of( @@ -2039,7 +1954,7 @@ public boolean hasSameBitWidth() { return hasSameBitWidth; } - public Optional compute(List args) { + public Optional compute(List constArgs, List args) { logger.atWarn().log("Computation of constants for built-in {} is not implemented", this); return Optional.empty(); } @@ -2057,10 +1972,11 @@ public boolean isStatusBuiltin() { * in the built-in definition, and if not, if it is possible to produce a type of the * parameter type class that can be trivially cast from the argument type. * - * @param argTypes of the concrete arguments + * @param constArgs the concrete constant arguments + * @param argTypes of the concrete arguments * @return true if argument types are correct, false otherwise */ - public boolean takes(List argTypes) { + public boolean takes(List constArgs, List argTypes) { if (this.signature().hasVarArgs()) { if (argTypes.size() < argTypeClasses().size()) { @@ -2086,7 +2002,13 @@ public boolean takes(List argTypes) { return false; } - return argsCompatible(argTypes, argTypeClasses()); + if (constArgTypeClasses().size() != constArgs.size()) { + // if the number of constant arguments is not correct, this can't be true + return false; + } + + return argsCompatible(argTypes, argTypeClasses()) + && argsCompatible(constArgs.stream().map(Constant::type).toList(), constArgTypeClasses()); } private boolean argsCompatible(List argTypes, @@ -2123,15 +2045,29 @@ private boolean argsCompatible(List argTypes, return true; } + /** + * Returns the result type of the built-in when called with the given argument types and + * constant arguments. + * It assumes that the argument types are valid, such as a call to + * {@link #takes(List, List)} would return true. + * + * @param constArgs concrete constant arguments. + * @param argTypes concrete types of argument for call. + * @return the concrete type that is return by this built-in + */ + public abstract Type returns(List constArgs, List argTypes); + /** * Returns the result type of the built-in when called with the given argument types. * It assumes that the argument types are valid, such as a call to - * {@link #takes(List)} would return true. + * {@link #takes(List, List)} would return true. * * @param argTypes concrete types of argument for call. * @return the concrete type that is return by this built-in */ - public abstract Type returns(List argTypes); + public Type returns(List argTypes) { + return returns(List.of(), argTypes); + } public final boolean matches(RelationType type) { @@ -2143,6 +2079,10 @@ public String toString() { return name + signature; } + public List> constArgTypeClasses() { + return signature.constArgTypeClass(); + } + public List> argTypeClasses() { return signature.argTypeClasses(); } @@ -2203,11 +2143,11 @@ public static class BuiltInBuilder { private RelationType signature; private BuiltIn.Kind kind; @Nullable - private Function, Optional> computeFunction; + private BiFunction, List, Optional> computeFunction; @Nullable - private Function, Boolean> takesFunction; + private BiFunction, List, Boolean> takesFunction; @Nullable - private Function, Type> returnsFunction; + private BiFunction, List, Type> returnsFunction; private boolean hasSameBitWidth = false; BuiltInBuilder(String name, @Nullable String operator, RelationType signature, @@ -2220,52 +2160,74 @@ public static class BuiltInBuilder { public BuiltInBuilder computeUnary( Function computeFunction) { - this.computeFunction = - (args) -> Optional.of(computeFunction.apply((T) args.get(0))); + this.computeFunction = (constArgs, args) -> + Optional.of(computeFunction.apply((T) args.get(0))); return this; } public BuiltInBuilder compute( Function, R> computeFunction) { - this.computeFunction = - (args) -> Optional.of(computeFunction.apply(args.stream().map(a -> (T) a).toList())); + this.computeFunction = (constArgs, args) -> + Optional.of(computeFunction.apply(args.stream().map(a -> (T) a).toList())); return this; } public BuiltInBuilder compute( BiFunction computeFunction) { - this.computeFunction = - (args) -> Optional.of(computeFunction.apply((A) args.get(0), (B) args.get(1))); + this.computeFunction = (constArgs, args) -> + Optional.of(computeFunction.apply((A) args.get(0), (B) args.get(1))); return this; } @SuppressWarnings("LineLength") public BuiltInBuilder compute( TriFunction computeFunction) { - this.computeFunction = - (args) -> Optional.of( - computeFunction.apply((A) args.get(0), (B) args.get(1), (C) args.get(2))); + this.computeFunction = (constArgs, args) -> + Optional.of(computeFunction.apply((A) args.get(0), (B) args.get(1), (C) args.get(2))); return this; } public BuiltInBuilder noCompute() { - this.computeFunction = (args) -> Optional.empty(); + this.computeFunction = (constArgs, args) -> Optional.empty(); return this; } - public BuiltInBuilder takesData(Function, Boolean> takesFunction) { - this.takesFunction = (args) -> args.stream().allMatch(DataType.class::isInstance) - && takesFunction.apply(args.stream().map(DataType.class::cast).toList()); + public BuiltInBuilder takesData( + BiFunction, List, Boolean> takesFunction) { + this.takesFunction = (constArgs, args) -> args.stream().allMatch(DataType.class::isInstance) + && takesFunction.apply(constArgs, args.stream().map(DataType.class::cast).toList()); return this; } + public BuiltInBuilder takesData(Function, Boolean> takesFunction) { + return takesData((constArgs, args) -> takesFunction.apply(args)); + } + + public BuiltInBuilder takesDataFromFirstFloatSize( + BiFunction, Boolean> takesFunction) { + return takesData((constArgs, args) -> { + ensure(!constArgs.isEmpty(), "Expected at least one constant argument, but found none."); + return takesFunction.apply(floatTypeSize(constArgs.get(0)), args); + }); + } + + public BuiltInBuilder takesDataFromFirstTwoFloatSizes( + TriFunction, Boolean> takesFunction) { + return takesData((constArgs, args) -> { + ensure(constArgs.size() >= 2, "Expected at least two constant argument, but found %d.", + constArgs.size()); + return takesFunction.apply( + floatTypeSize(constArgs.get(0)), floatTypeSize(constArgs.get(1)), args); + }); + } + /** - * This will use the default implementation of {@link BuiltIn#takes(List)}. + * This will use the default implementation of {@link BuiltIn#takes(List, List)}. * So it will compare type classes and checks if an argument is trivially cast * to a parameter's type class. */ public BuiltInBuilder takesDefault() { - this.takesFunction = (args) -> true; + this.takesFunction = (constArgs, args) -> true; return this; } @@ -2283,30 +2245,19 @@ public BuiltInBuilder takesFirstTwoWithSameBitWidths() { return this; } - public BuiltInBuilder takesFrm(int roundingModeArgIdx) { - takesData((args) -> args.size() > roundingModeArgIdx - && args.get(roundingModeArgIdx).bitWidth() == 3 - ); - return this; + public BuiltInBuilder takesFloatArgs(int floatArgCount) { + return takesDataFromFirstFloatSize((size, args) -> args.size() == floatArgCount + && args.stream().limit(floatArgCount).allMatch(a -> a.bitWidth() == size)); } - public BuiltInBuilder takesFirstTwoWithSameBitWidthsAndFrm(int roundingModeArgIdx) { - takesData((args) -> args.size() > roundingModeArgIdx - && args.get(0).bitWidth() == args.get(1).bitWidth() - && args.get(roundingModeArgIdx).bitWidth() == 3 - ); - this.hasSameBitWidth = true; - return this; + public BuiltInBuilder takesFloatArgsAndFrm(int floatArgCount) { + return takesDataFromFirstFloatSize((size, args) -> args.size() == floatArgCount + 1 + && args.stream().limit(floatArgCount).allMatch(a -> a.bitWidth() == size) + && args.get(floatArgCount).bitWidth() == 3); } - public BuiltInBuilder takesFirstThreeWithSameBitWidthsAndFrm(int roundingModeArgIdx) { - takesData((args) -> args.size() > roundingModeArgIdx - && args.get(0).bitWidth() == args.get(1).bitWidth() - && args.get(0).bitWidth() == args.get(2).bitWidth() - && args.get(roundingModeArgIdx).bitWidth() == 3 - ); - this.hasSameBitWidth = true; - return this; + public BuiltInBuilder takesFrm(int frmArgIdx) { + return takesData(args -> args.size() == frmArgIdx + 1 && args.get(frmArgIdx).bitWidth() == 3); } public BuiltInBuilder returns(Type returnType) { @@ -2315,6 +2266,11 @@ public BuiltInBuilder returns(Type returnType) { } public BuiltInBuilder returns(Function, Type> returnsFunction) { + returns((constArgs, args) -> returnsFunction.apply(args)); + return this; + } + + public BuiltInBuilder returns(BiFunction, List, Type> returnsFunction) { this.returnsFunction = returnsFunction; return this; } @@ -2323,17 +2279,25 @@ public BuiltInBuilder returnsFirstBitWidth(Class returnT returnsFromFirstAsDataType( (firstDataType) -> { var result = constructDataType(returnTypeClass, firstDataType.bitWidth()); - Objects.requireNonNull(result); + requireNonNull(result); return result; }); return this; } + public BuiltInBuilder returnsFirstFloatType() { + return returnsFromFirstFloatSize(BitsType::bits); + } + + public BuiltInBuilder returnsSecondFloatType() { + return returnsFromFirstTwoFloatSizes((s0, s1) -> BitsType.bits(s1)); + } + public BuiltInBuilder returnsFirstBitWidthAndStatus( Class returnTypeClass) { returnsFromFirstAsDataType((firstDataType) -> { var valType = constructDataType(returnTypeClass, firstDataType.bitWidth()); - Objects.requireNonNull(valType); + requireNonNull(valType); return Type.struct( BUILTIN_RESULT, valType, BUILTIN_STATUS, Type.status() @@ -2342,18 +2306,6 @@ public BuiltInBuilder returnsFirstBitWidthAndStatus( return this; } - public BuiltInBuilder returnsFirstBitWidthAndFloatStatus() { - returnsFromFirstAsDataType((firstDataType) -> { - var valType = constructDataType(BitsType.class, firstDataType.bitWidth()); - Objects.requireNonNull(valType); - return Type.struct( - BUILTIN_RESULT, valType, - BUILTIN_STATUS, Type.floatStatus() - ); - }); - return this; - } - public BuiltInBuilder returnsFromFirstAsDataType(Function returnFunction) { returns((args) -> { @@ -2374,6 +2326,42 @@ public BuiltInBuilder returnsFromDataTypes(Function, Type> return return this; } + public BuiltInBuilder returnsFromSecondConstSize( + Class returnTypeClass) { + return returns((constArgs, args) -> { + ensure(constArgs.size() >= 2, "Expected at least two constant argument, but found %d.", + constArgs.size()); + return requireNonNull(constructDataType(returnTypeClass, constInt(constArgs.get(1)))); + }); + } + + public BuiltInBuilder returnsFromFirstFloatSize(Function returnFunction) { + return returns((constArgs, args) -> { + ensure(!constArgs.isEmpty(), "Expected at least one constant argument, but found none."); + return returnFunction.apply(floatTypeSize(constArgs.get(0))); + }); + } + + public BuiltInBuilder returnsFromFirstTwoFloatSizes( + BiFunction returnFunction) { + return returns((constArgs, args) -> { + ensure(constArgs.size() >= 2, "Expected at least two constant argument, but found %d.", + constArgs.size()); + return returnFunction.apply( + floatTypeSize(constArgs.get(0)), floatTypeSize(constArgs.get(1))); + }); + } + + private int floatTypeSize(Constant arg) { + ensure(arg instanceof Constant.FloatType, "Expected a float type, but found %s", arg); + return ((Constant.FloatType) arg).size(); + } + + private int constInt(Constant arg) { + ensure(arg instanceof Constant.Value, "Expected a value type, but found %s", arg); + return ((Constant.Value) arg).intValue(); + } + public BuiltIn build() { @@ -2389,39 +2377,37 @@ public BuiltIn build() { return new BuiltIn(name, operator, signature, kind, hasSameBitWidth) { @Override - public Optional compute(List args) { + public Optional compute(List constArgs, List args) { if (computeFunction == null) { - return super.compute(args); + return super.compute(constArgs, args); } - var argTypes = args.stream() - .map(Constant::type) - .toList(); - if (!takes(argTypes)) { + var argTypes = args.stream().map(Constant::type).toList(); + if (!takes(constArgs, argTypes)) { throw new ViamError("Types of arguments does not match type signature of " + signature) .addContext("built-in", this) - .addContext("constants", List.of(args)); + .addContext("constants", List.of(constArgs, args)); } - return computeFunction.apply(args) + return computeFunction.apply(constArgs, args) .map(result -> result instanceof Constant.Value value - ? value.trivialCastTo(returns(argTypes)) + ? value.trivialCastTo(returns(constArgs, argTypes)) : result); } @Override - public boolean takes(List argTypes) { + public boolean takes(List constArgs, List argTypes) { // always check general case first - var generalConstraintsValid = super.takes(argTypes); + var generalConstraintsValid = super.takes(constArgs, argTypes); if (generalConstraintsValid) { // if general case doesn't fail, then test specific constraints - return takesFunction.apply(argTypes); + return takesFunction.apply(constArgs, argTypes); } return false; } @Override - public Type returns(List argTypes) { - return returnsFunction.apply(argTypes); + public Type returns(List constArgs, List argTypes) { + return returnsFunction.apply(constArgs, argTypes); } }; } diff --git a/vadl/main/vadl/types/RelationType.java b/vadl/main/vadl/types/RelationType.java index 18f265885..d6bac31ed 100644 --- a/vadl/main/vadl/types/RelationType.java +++ b/vadl/main/vadl/types/RelationType.java @@ -18,8 +18,6 @@ import java.util.List; import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; /** * Represents a relation type in VADL's type system. @@ -30,16 +28,16 @@ public class RelationType extends Type { private final List> argTypeClass; + private final List> constArgTypeClass; private final boolean hasVarArgs; - private final int floatTypeArgCount; private final Class resultTypeClass; - protected RelationType(List> argTypes, boolean hasVarArgs, - int floatTypeArgCount, + protected RelationType(List> argTypes, + List> constArgTypeClass, boolean hasVarArgs, Class resultType) { this.argTypeClass = argTypes; + this.constArgTypeClass = constArgTypeClass; this.hasVarArgs = hasVarArgs; - this.floatTypeArgCount = floatTypeArgCount; this.resultTypeClass = resultType; } @@ -47,12 +45,12 @@ public List> argTypeClasses() { return argTypeClass; } - public boolean hasVarArgs() { - return hasVarArgs; + public List> constArgTypeClass() { + return constArgTypeClass; } - public int floatTypeArgCount() { - return floatTypeArgCount; + public boolean hasVarArgs() { + return hasVarArgs; } public Class resultTypeClass() { @@ -61,22 +59,18 @@ public Class resultTypeClass() { @Override public String name() { - return "(" - + argTypeClass.stream().map(Class::getSimpleName) - .collect(Collectors.joining(", ")) - + ") -> " - + resultTypeClass.getSimpleName(); - } - - /** - * A readable representation of the type, with the {@link FloatType} arguments. - */ - public String nameWithFloatTypes() { - return "(" - + Stream.concat( - IntStream.range(0, floatTypeArgCount).mapToObj(i -> Type.floatType().name()), - argTypeClass.stream().map(Class::getSimpleName)).collect(Collectors.joining(", ")) - + ") -> " - + resultTypeClass.getSimpleName(); + var sb = new StringBuilder(); + if (!constArgTypeClass.isEmpty()) { + sb.append("<"); + sb.append(constArgTypeClass.stream().map(Class::getSimpleName) + .collect(Collectors.joining(", "))); + sb.append(">"); + } + sb.append("("); + sb.append(argTypeClass.stream().map(Class::getSimpleName) + .collect(Collectors.joining(", "))); + sb.append(") -> "); + sb.append(resultTypeClass.getSimpleName()); + return sb.toString(); } } diff --git a/vadl/main/vadl/types/Type.java b/vadl/main/vadl/types/Type.java index 2c26784e3..3ca3b04cc 100644 --- a/vadl/main/vadl/types/Type.java +++ b/vadl/main/vadl/types/Type.java @@ -286,38 +286,53 @@ public static StringType string() { */ public static RelationType relation(List> argTypes, Class returnType) { - return relation(argTypes, false, 0, returnType); + return relation(argTypes, List.of(), false, returnType); } /** * Retrieves the generic relation type. * - * @param argTypes the list of argument type classes - * @param returnType the return type class + * @param argTypes the list of argument type classes + * @param constArgTypes the list of constant argument type classes + * @param returnType the return type class * @return the RelationType instance */ public static RelationType relation(List> argTypes, - int floatTypeArgCount, + List> constArgTypes, Class returnType) { - return relation(argTypes, false, floatTypeArgCount, returnType); + return relation(argTypes, constArgTypes, false, returnType); } /** * Retrieves the generic relation type. * - * @param argTypes the list of argument type classes - * @param hasVarArgs the flag indicating if the last argument of kind varargs - * @param floatTypeArgCount the amount of float-type arguments preceding other args - * @param returnType the return type class + * @param argTypes the list of argument type classes + * @param hasVarArgs the flag indicating if the last argument of kind varargs + * @param returnType the return type class + * @return the RelationType instance + */ + public static RelationType relation(List> argTypes, + boolean hasVarArgs, + Class returnType) { + return relation(argTypes, List.of(), hasVarArgs, returnType); + } + + /** + * Retrieves the generic relation type. + * + * @param argTypes the list of argument type classes + * @param constArgTypes the list of constant argument type classes + * @param hasVarArgs the flag indicating if the last argument of kind varargs + * @param returnType the return type class * @return the RelationType instance */ public static RelationType relation(List> argTypes, + List> constArgTypes, boolean hasVarArgs, - int floatTypeArgCount, Class returnType) { - var hashCode = Objects.hash(argTypes, hasVarArgs, floatTypeArgCount, returnType); + var hashCode = Objects.hash(argTypes, constArgTypes, hasVarArgs, returnType); return relationTypes.computeIfAbsent(hashCode, k -> - new RelationType(argTypes, hasVarArgs, floatTypeArgCount, returnType)); + new RelationType(argTypes, constArgTypes, hasVarArgs, returnType)); } /** @@ -327,7 +342,7 @@ public static RelationType relation(List> argTypes, * @return the RelationType instance */ public static RelationType relation(Class returnType) { - return relation(List.of(), false, 0, returnType); + return relation(List.of(), List.of(), false, returnType); } /** @@ -353,7 +368,7 @@ public static RelationType relation(Class argType, public static RelationType relation(Class firstArg, Class secondArg, Class returnType) { - return relation(List.of(firstArg, secondArg), false, 0, returnType); + return relation(List.of(firstArg, secondArg), List.of(), false, returnType); } private static final HashMap concreteRelationTypes = diff --git a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java index 687849ec5..66d7c125f 100644 --- a/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInEmptyNoStatusDispatcher.java @@ -249,59 +249,23 @@ default void handleFEQ(T input) { } @Override - default void handleFCVTFF(T input) { + default void handleFCVT(T input) { } @Override - default void handleFCVTFF2(T input) { + default void handleFCVTFS(T input) { } @Override - default void handleFCVTFSS(T input) { + default void handleFCVTFU(T input) { } @Override - default void handleFCVTFSD(T input) { + default void handleFCVTSF(T input) { } @Override - default void handleFCVTFUS(T input) { - } - - @Override - default void handleFCVTFUD(T input) { - } - - @Override - default void handleFCVTSSF(T input) { - } - - @Override - default void handleFCVTSDF(T input) { - } - - @Override - default void handleFCVTUSF(T input) { - } - - @Override - default void handleFCVTUDF(T input) { - } - - @Override - default void handleFCVTSSF2(T input) { - } - - @Override - default void handleFCVTSDF2(T input) { - } - - @Override - default void handleFCVTUSF2(T input) { - } - - @Override - default void handleFCVTUDF2(T input) { + default void handleFCVTUF(T input) { } @Override diff --git a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java index 606ae1e51..1f38cc4fb 100644 --- a/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java +++ b/vadl/main/vadl/utils/VadlBuiltInNoStatusDispatcher.java @@ -141,34 +141,16 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { handleFLE(input); } else if (builtIn == BuiltInTable.FEQ) { handleFEQ(input); - } else if (builtIn == BuiltInTable.FCVTFF) { - handleFCVTFF(input); - } else if (builtIn == BuiltInTable.FCVTFF2) { - handleFCVTFF2(input); - } else if (builtIn == BuiltInTable.FCVTFSS) { - handleFCVTFSS(input); - } else if (builtIn == BuiltInTable.FCVTFSD) { - handleFCVTFSD(input); - } else if (builtIn == BuiltInTable.FCVTFUS) { - handleFCVTFUS(input); - } else if (builtIn == BuiltInTable.FCVTFUD) { - handleFCVTFUD(input); - } else if (builtIn == BuiltInTable.FCVTSSF) { - handleFCVTSSF(input); - } else if (builtIn == BuiltInTable.FCVTSDF) { - handleFCVTSDF(input); - } else if (builtIn == BuiltInTable.FCVTUSF) { - handleFCVTUSF(input); - } else if (builtIn == BuiltInTable.FCVTUDF) { - handleFCVTUDF(input); - } else if (builtIn == BuiltInTable.FCVTSSF2) { - handleFCVTSSF2(input); - } else if (builtIn == BuiltInTable.FCVTSDF2) { - handleFCVTSDF2(input); - } else if (builtIn == BuiltInTable.FCVTUSF2) { - handleFCVTUSF2(input); - } else if (builtIn == BuiltInTable.FCVTUDF2) { - handleFCVTUDF2(input); + } else if (builtIn == BuiltInTable.FCVT) { + handleFCVT(input); + } else if (builtIn == BuiltInTable.FCVTFS) { + handleFCVTFS(input); + } else if (builtIn == BuiltInTable.FCVTFU) { + handleFCVTFU(input); + } else if (builtIn == BuiltInTable.FCVTSF) { + handleFCVTSF(input); + } else if (builtIn == BuiltInTable.FCVTUF) { + handleFCVTUF(input); } else if (builtIn == BuiltInTable.FISINF) { handleFISINF(input); } else if (builtIn == BuiltInTable.FISZERO) { @@ -301,33 +283,15 @@ default boolean dispatch(T input, BuiltInTable.BuiltIn builtIn) { void handleFEQ(T input); - void handleFCVTFF(T input); + void handleFCVT(T input); - void handleFCVTFF2(T input); + void handleFCVTFS(T input); - void handleFCVTFSS(T input); + void handleFCVTFU(T input); - void handleFCVTFSD(T input); + void handleFCVTSF(T input); - void handleFCVTFUS(T input); - - void handleFCVTFUD(T input); - - void handleFCVTSSF(T input); - - void handleFCVTSDF(T input); - - void handleFCVTUSF(T input); - - void handleFCVTUDF(T input); - - void handleFCVTSSF2(T input); - - void handleFCVTSDF2(T input); - - void handleFCVTUSF2(T input); - - void handleFCVTUDF2(T input); + void handleFCVTUF(T input); void handleFISINF(T input); diff --git a/vadl/main/vadl/viam/Constant.java b/vadl/main/vadl/viam/Constant.java index 38a33828f..9058ae58e 100644 --- a/vadl/main/vadl/viam/Constant.java +++ b/vadl/main/vadl/viam/Constant.java @@ -16,6 +16,7 @@ package vadl.viam; +import static java.util.Objects.requireNonNull; import static vadl.error.Diagnostic.warning; import static vadl.types.BuiltInTable.BUILTIN_RESULT; import static vadl.types.BuiltInTable.BUILTIN_STATUS; @@ -41,7 +42,9 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; +import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.jetbrains.annotations.Contract; import vadl.error.DeferredDiagnosticStore; import vadl.types.BitsType; @@ -443,7 +446,7 @@ public Constant.Value multiply(Constant.Value other, boolean longVersion, boolea .multiply(b.integer()); // multiply with other value var newType = Type.constructDataType(divType.getClass(), 2 * divType.bitWidth()); - Objects.requireNonNull(newType); + requireNonNull(newType); return fromInteger(newValue, newType); } else { @@ -467,7 +470,7 @@ public Constant.Value divide(Constant.Value other, boolean signed) { var divType = signed ? Type.signedInt(type().bitWidth()) : Type.unsignedInt(type().bitWidth()); - Objects.requireNonNull(divType); + requireNonNull(divType); var a = this.trivialCastTo(divType); var b = other.trivialCastTo(divType); @@ -510,7 +513,7 @@ public Constant.Value modulo(Constant.Value other, boolean signed) { var divType = signed ? Type.signedInt(type().bitWidth()) : Type.unsignedInt(type().bitWidth()); - Objects.requireNonNull(divType); + requireNonNull(divType); var a = this.trivialCastTo(divType); var b = other.trivialCastTo(divType); @@ -1412,7 +1415,7 @@ public Map values() { * @return The Constant value at the specified name. */ public Constant get(String name) { - return Objects.requireNonNull(values.get(name), + return requireNonNull(values.get(name), "Struct does not contain a value with name %s".formatted(name)); } @@ -1551,6 +1554,82 @@ public Constant.Value overflow() { } } + /** + * Represents a constant float-type. + * + *

    It stores a reference to the {@link FloatFormat}. + */ + public static class FloatType extends Constant { + + @Nullable + private final FloatFormat format; + + private final Integer size; + private final String name; + + /** + * Constructs a float-type constant from a float format definition. + * + * @param format The float format definition. + */ + public FloatType(FloatFormat format) { + super(Type.floatType()); + this.format = format; + // the type-checker checks that float-type definitions have a size, and thus an encoding + this.size = requireNonNull(format.encoding()).size; + this.name = format.simpleName(); + } + + /** + * Constructs a dummy float-type constant from a float format encoding size. This is used by + * the type checker when the float format definition is not yet available. + * + * @param size The float format encoding size, i.e. the bit-size of the float type. + * @param name The name of the float format. + */ + public FloatType(int size, String name) { + super(Type.floatType()); + this.format = null; + this.size = size; + this.name = name; + } + + @Nullable + public FloatFormat format() { + return format; + } + + public Integer size() { + return size; + } + + @Override + public java.lang.String toString() { + return name + ": " + type().toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + FloatType floatType = (FloatType) o; + return Objects.equals(format, floatType.format) && Objects.equals(size, + floatType.size) && Objects.equals(name, floatType.name); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), format, size, name); + } + } + // HELPER FUNCTIONS diff --git a/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java b/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java index 187f80794..1af5d47e5 100644 --- a/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java +++ b/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java @@ -23,6 +23,7 @@ import vadl.types.BuiltInTable; import vadl.types.BuiltInTable.BuiltIn; import vadl.types.Type; +import vadl.viam.Constant; import vadl.viam.graph.Canonicalizable; import vadl.viam.graph.GraphNodeVisitor; import vadl.viam.graph.Node; @@ -40,9 +41,19 @@ public class BuiltInCall extends AbstractFunctionCallNode implements Canonicaliz @DataValue protected BuiltIn builtIn; - public BuiltInCall(BuiltIn builtIn, NodeList args, Type type) { + @DataValue + protected List constArgs; + + @SuppressWarnings("checkstyle:MissingJavadocMethod") + public BuiltInCall(BuiltIn builtIn, List constArgs, + NodeList args, Type type) { super(args, type); this.builtIn = builtIn; + this.constArgs = constArgs; + } + + public BuiltInCall(BuiltIn builtIn, NodeList args, Type type) { + this(builtIn, List.of(), args, type); } /** @@ -78,6 +89,17 @@ public BuiltIn builtIn() { return this.builtIn; } + /** + * Gets the constant args, i.e. the args in the pointy brackets {@code <...>}. + */ + public List constArgs() { + return constArgs; + } + + public void setConstArgs(List constArgs) { + this.constArgs = constArgs; + } + public ExpressionNode arg(int index) { return args.get(index); } @@ -96,7 +118,7 @@ public Node canonical() { .toList(); return builtIn - .compute(args) + .compute(constArgs, args) .map(e -> (Node) new ConstantNode(e)) .orElse(this); } @@ -130,11 +152,11 @@ public void verifyState() { "Number of arguments must match, %s vs %s", argTypeClasses.size(), this.arguments().size()); var actualArgTypes = this.arguments().stream().map(ExpressionNode::type).toList(); - ensure(builtIn.takes(actualArgTypes), - "Arguments' types do not match with the type of the builtin. Args: %s", - actualArgTypes); + ensure(builtIn.takes(constArgs, actualArgTypes), + "Arguments' types do not match with the type of the builtin. Const args: %s, Args: %s", + constArgs, actualArgTypes); - var builtInResultType = builtIn.returns(actualArgTypes); + var builtInResultType = builtIn.returns(constArgs, actualArgTypes); ensure(builtInResultType.isTrivialCastTo(this.type()), "BuiltIns' result type does not match node's type. %s vs %s", builtInResultType, this.type() ); @@ -143,13 +165,14 @@ public void verifyState() { @Override public ExpressionNode copy() { return new BuiltInCall(builtIn, + constArgs.stream().toList(), new NodeList<>(this.arguments().stream().map(x -> (ExpressionNode) x.copy()).toList()), this.type()); } @Override public Node shallowCopy() { - return new BuiltInCall(builtIn, args, type()); + return new BuiltInCall(builtIn, constArgs, args, type()); } @@ -157,6 +180,7 @@ public Node shallowCopy() { protected void collectData(List collection) { super.collectData(collection); collection.add(builtIn); + collection.add(constArgs); } @Override @@ -169,6 +193,16 @@ public void prettyPrint(StringBuilder sb) { sb.append(")"); } else { sb.append(builtIn.name()); + if (!constArgs.isEmpty()) { + sb.append("<"); + for (int i = 0; i < constArgs.size(); i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(constArgs.get(i).toString()); + } + sb.append(">"); + } sb.append("("); for (int i = 0; i < args.size(); i++) { diff --git a/vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java b/vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java deleted file mode 100644 index f3bf7a422..000000000 --- a/vadl/main/vadl/viam/graph/dependency/FloatBuiltInCall.java +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-FileCopyrightText : © 2025 TU Wien -// SPDX-License-Identifier: GPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package vadl.viam.graph.dependency; - -import static java.util.Collections.reverse; - -import java.util.List; -import vadl.javaannotations.viam.DataValue; -import vadl.types.BuiltInTable; -import vadl.types.BuiltInTable.BuiltIn; -import vadl.types.FloatType; -import vadl.types.Type; -import vadl.viam.FloatFormat; -import vadl.viam.graph.Canonicalizable; -import vadl.viam.graph.GraphNodeVisitor; -import vadl.viam.graph.Node; -import vadl.viam.graph.NodeList; - -/** - * Represents a function call to a VADL float built-in. - * It holds a {@link BuiltIn} function from the {@link BuiltInTable} and - * extends {@link BuiltInCall}. - * - * @see BuiltInCall - * @see BuiltInTable - * @see AbstractFunctionCallNode - */ -public class FloatBuiltInCall extends BuiltInCall { - - @DataValue - protected List formats; - - public FloatBuiltInCall(BuiltIn builtIn, NodeList args, - List formats, Type type) { - super(builtIn, args, type); - this.formats = formats; - } - - public List formats() { - return formats; - } - - @Override - public void verifyState() { - super.verifyState(); - ensure(builtIn.signature().floatTypeArgCount() == formats().size(), - "Number of float types must match, %s vs %s", - builtIn.signature().floatTypeArgCount(), formats().size()); - } - - @Override - public ExpressionNode copy() { - return new FloatBuiltInCall(builtIn, - new NodeList<>(arguments().stream().map(ExpressionNode::copy).toList()), - formats(), - type()); - } - - @Override - public Node shallowCopy() { - return new FloatBuiltInCall(builtIn, args, formats(), type()); - } - - - @Override - protected void collectData(List collection) { - super.collectData(collection); - collection.add(formats); - } - - @Override - public void prettyPrint(StringBuilder sb) { - sb.append(builtIn.name()); - sb.append("("); - - for (int i = 0; i < args.size(); i++) { - if (i > 0) { - sb.append(", "); - } - args.get(i).prettyPrint(sb); - } - - for (int i = 0; i < formats.size(); i++) { - if (i > 0 || !args.isEmpty()) { - sb.append(", "); - } - formats.get(i).simpleName(); - } - - sb.append(")"); - } -} From 2d85f3de9999254d06e2a1e514c90c557575b550 Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Fri, 31 Jul 2026 16:49:49 +0200 Subject: [PATCH 10/11] wip: Add IEEE rounding modes to ISS --- sys/risc-v/rv64fd.vadl | 122 +++++++++++------- .../templates/iss/target/gen-arch/helper.c | 50 ++++--- .../templates/iss/target/gen-arch/helper.h | 28 ++-- .../tcg/lowering/TcgOpLoweringPass.java | 33 ++--- 4 files changed, 136 insertions(+), 97 deletions(-) diff --git a/sys/risc-v/rv64fd.vadl b/sys/risc-v/rv64fd.vadl index 51c729ee6..89760638b 100644 --- a/sys/risc-v/rv64fd.vadl +++ b/sys/risc-v/rv64fd.vadl @@ -3,7 +3,7 @@ import rv64csr::{RV64IZicsr} instruction set architecture RV64IFD extending RV64IZicsr = { - // TODO: generate illegal instruction ex + // TODO: generate illegal instruction exception when float is not enabled model ExtensionFD () : Id = {F} model InstructionsFD (f : IsaDefs, d : IsaDefs) : IsaDefs = { @@ -76,15 +76,16 @@ instruction set architecture RV64IFD extending RV64IZicsr = { , q = 0b11 // quad precision (128-bit) } - function FrmName(frm : Bits3) -> String = - match frm with - { Frm::rne => "rne" - , Frm::rtz => "rtz" - , Frm::rdn => "rdn" - , Frm::rup => "rup" - , Frm::rmm => "rmm" - , _ => "" - } + model FrmName(rm : Ex) : Str = { + match : Str + ( $rm = Frm::rne => "rne" + ; $rm = Frm::rtz => "rtz" + ; $rm = Frm::rdn => "rdn" + ; $rm = Frm::rup => "rup" + ; $rm = Frm::rmm => "rmm" + ; _ => "" + ) + } format FRtype : Inst = // Rtype register 3 operand instruction format (for float ops) { funct5 : Bits5 // [31..27] 5 bit function code @@ -120,7 +121,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { } model FRtypeInstr1rm (c : FRtypeRec, rs2 : Bin) : IsaDefs = { - $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", FrmName($c.rm))) + $FRtypeInstr($c; rs2 = $rs2; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", $FrmName($c.rm))) } model FRtypeInstr2 (c : FRtypeRec) : IsaDefs = { @@ -128,7 +129,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { } model FRtypeInstr2rm (c : FRtypeRec) : IsaDefs = { - $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($c.rm))) + $FRtypeInstr($c; none; assembly $c.name = ($c.mne, " ", register(rd), ",", register(rs1), ",", register(rs2), ",", $FrmName($c.rm))) } [ canonical sNaN : 0x7fa00000 ] @@ -178,15 +179,25 @@ instruction set architecture RV64IFD extending RV64IZicsr = { model Unsigned () : BoolModelRec = {(UnsignedId ; UnsignedStr)} model Signed () : BoolModelRec = {(SignedId ; SignedStr )} - model FRtypeInstrBiArith (name : Id, size : FSizeRec, fun : SymEx, funct5 : Bin, rm : Ex) : IsaDefs = { + function frm(rm : Bits3) -> Bits3 = + match (match rm with { Frm::dyn => FCSR.frm , _ => rm }) with + { Frm::rne => 0 // these are QEMU's rounding mode encodings, we should use our own + , Frm::rtz => 3 + , Frm::rdn => 1 + , Frm::rup => 2 + , Frm::rmm => 4 + , _ => 0 // this should never happen + } + + model FRtypeInstrBiArith (name : Id, size : FSizeRec, fun : Id, funct5 : Bin, rm : Ex) : IsaDefs = { $FRtypeInstr2rm (( - AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + AsId($name, $size.suffix, $FrmName($rm)) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; $funct5 ; - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, $rm)) + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, frm($rm))) )) } - model FRtypeInstrMinMax (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + model FRtypeInstrMinMax (name : Id, size : FSizeRec, fun : Id, rm : Ex) : IsaDefs = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b0'0101 ; @@ -194,15 +205,15 @@ instruction set architecture RV64IFD extending RV64IZicsr = { )) } - model FRtypeInstrSqrt (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + model FRtypeInstrSqrt (name : Id, size : FSizeRec, fun : Id, rm : Ex) : IsaDefs = { $FRtypeInstr1rm (( - AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; + AsId($name, $size.suffix, $FrmName($rm)) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b0'1011 ; - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, $rm)) + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, frm($rm))) ) ; 0b0'0000 ) } - model FRtypeInstrCmp (name : Id, size : FSizeRec, fun : SymEx, rm : Ex) : IsaDefs = { + model FRtypeInstrCmp (name : Id, size : FSizeRec, fun : Id, rm : Ex) : IsaDefs = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b1'0100 ; @@ -238,31 +249,31 @@ instruction set architecture RV64IFD extending RV64IZicsr = { ) ; 0b0'0000 ) } - model FRtypeInstrCvtX2F (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, rs2 : Bin, rm : Ex) : IsaDefs = { + model FRtypeInstrCvtX2F (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, rs2 : Bin, rm : Ex) : IsaDefs = { $FRtypeInstr1rm (( - AsId($name, $size.suffix, $iSize.cvtSuffix, $u.str("U" ; "")) ; + AsId($name, $size.suffix, $iSize.cvtSuffix, $u.str("U" ; ""), $FrmName($rm)) ; AsStr($name, ".", $size.suffix, ".", $iSize.mvSuffix, $u.str("U" ; "")) ; $rm ; $size.fmt ; 0b1'1010 ; - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), $rm)) + F(rd) := $NaNBox($size ; VADL::$u.id(fcvtuf ; fcvtsf)<$size.fTy>(X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), frm($rm))) ) ; $rs2 ) } - model FRtypeInstrCvtF2X (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, fun : SymEx, rs2 : Bin, rm : Ex) : IsaDefs = { + model FRtypeInstrCvtF2X (name : Id, size : FSizeRec, iSize : FSizeRec, u : BoolModelRec, rs2 : Bin, rm : Ex) : IsaDefs = { $FRtypeInstr1rm (( - AsId($name, $iSize.cvtSuffix, $u.str("U" ; ""), $size.suffix) ; + AsId($name, $iSize.cvtSuffix, $u.str("U" ; ""), $size.suffix, $FrmName($rm)) ; AsStr($name, ".", $iSize.mvSuffix, $u.str("U" ; ""), ".", $size.suffix) ; $rm ; $size.fmt ; 0b1'1000 ; // always sign extend the result, even unsigned results - X(rd) := VADL::$fun<$size.fTy, $iSize.size>(F(rs1) as $size.ty, $rm) as SIntR + X(rd) := VADL::$u.id(fcvtfu ; fcvtfs)<$size.fTy, $iSize.size>(F(rs1) as $size.ty, frm($rm)) as SIntR ) ; $rs2 ) } - model FRtypeInstrCvtF2F (name : Id, iSize : FSizeRec, size : FSizeRec, fun : SymEx, rs2 : Bin, rm : Ex) : IsaDefs = { + model FRtypeInstrCvtF2F (name : Id, iSize : FSizeRec, size : FSizeRec, rs2 : Bin, rm : Ex) : IsaDefs = { $FRtypeInstr1rm (( - AsId($name, $size.suffix, $iSize.suffix) ; + AsId($name, $size.suffix, $iSize.suffix, $FrmName($rm)) ; AsStr($name, ".", $size.suffix, ".", $iSize.suffix) ; $rm ; $size.fmt ; 0b0'1000 ; - F(rd) := $NaNBox($size ; VADL::$fun<$iSize.fTy, $size.fTy>(F(rs1) as $iSize.ty, $rm)) + F(rd) := $NaNBox($size ; VADL::fcvt<$iSize.fTy, $size.fTy>(F(rs1) as $iSize.ty, frm($rm))) ) ; $rs2 ) } @@ -300,11 +311,11 @@ instruction set architecture RV64IFD extending RV64IZicsr = { assembly $name = (mnemonic, " ", register(rs2), ",", decimal(imm as SInt<12>), "(", register(rs1), ")") } - model FR4typeInstr (name : Id, size : FSizeRec, fun : SymEx, rm : Ex, opcode : Bin) : IsaDefs = { - instruction AsId($name, $size.suffix) : R4type = - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, F(rs3) as $size.ty, $rm)) - encoding AsId($name, $size.suffix) = {opcode = $opcode, funct3 = $rm, fmt = $size.fmt} - assembly AsId($name, $size.suffix) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", FrmName($rm)) + model FR4typeInstr (name : Id, size : FSizeRec, fun : Id, rm : Ex, opcode : Bin) : IsaDefs = { + instruction AsId($name, $size.suffix, $FrmName($rm)) : R4type = + F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, F(rs3) as $size.ty, frm($rm))) + encoding AsId($name, $size.suffix, $FrmName($rm)) = {opcode = $opcode, funct3 = $rm, fmt = $size.fmt} + assembly AsId($name, $size.suffix, $FrmName($rm)) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", $FrmName($rm)) } model CommonRoundedInstrs (size : FSizeRec, rm : Ex) : IsaDefs = { @@ -320,20 +331,28 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FR4typeInstr (FNMADD ; $size ; fnmadd ; $rm ; 0b100'1111) $FR4typeInstr (FNMSUB ; $size ; fnmsub ; $rm ; 0b100'1011) - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; fcvtsf ; 0b0'0000 ; $rm) - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; fcvtuf ; 0b0'0001 ; $rm) - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; fcvtsf ; 0b0'0010 ; $rm) - $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; fcvtuf ; 0b0'0011 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; 0b0'0000 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; 0b0'0001 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; 0b0'0010 ; $rm) + $FRtypeInstrCvtX2F (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; 0b0'0011 ; $rm) - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; fcvtfs ; 0b0'0000 ; $rm) - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; fcvtfu ; 0b0'0001 ; $rm) - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; fcvtfs ; 0b0'0010 ; $rm) - $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; fcvtfu ; 0b0'0011 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (SignedId ; SignedStr) ; 0b0'0000 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize32 ; (UnsignedId ; UnsignedStr) ; 0b0'0001 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (SignedId ; SignedStr) ; 0b0'0010 ; $rm) + $FRtypeInstrCvtF2X (FCVT ; $size ; $FSize64 ; (UnsignedId ; UnsignedStr) ; 0b0'0011 ; $rm) } - model CommonInstrs (size : FSizeRec) : IsaDefs = { - // TODO: add other rounding modes + model AllCommonRoundedInstrs (size : FSizeRec) : IsaDefs = { $CommonRoundedInstrs ($size ; Frm::rne) + $CommonRoundedInstrs ($size ; Frm::rtz) + $CommonRoundedInstrs ($size ; Frm::rdn) + $CommonRoundedInstrs ($size ; Frm::rup) + $CommonRoundedInstrs ($size ; Frm::rmm) + $CommonRoundedInstrs ($size ; Frm::dyn) + } + + model CommonInstrs (size : FSizeRec) : IsaDefs = { + $AllCommonRoundedInstrs ($size) $FRtypeInstrMinMax (FMIN ; $size ; fmin ; 0b000) $FRtypeInstrMinMax (FMAX ; $size ; fmax ; 0b001) @@ -361,8 +380,19 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FLtypeInstr (FLD ; $FSize64 ; 0b011) $FStypeInstr (FSD ; $FSize64 ; 0b011) $CommonInstrs ($FSize64) - $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; fcvt ; 0b0'0000 ; Frm::rne) - $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; fcvt ; 0b0'0001 ; Frm::rne) + // TODO: shorten this + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; 0b0'0000 ; Frm::rne) + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; 0b0'0000 ; Frm::rtz) + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; 0b0'0000 ; Frm::rdn) + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; 0b0'0000 ; Frm::rup) + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; 0b0'0000 ; Frm::rmm) + $FRtypeInstrCvtF2F (FCVT ; $FSize32 ; $FSize64 ; 0b0'0000 ; Frm::dyn) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; 0b0'0001 ; Frm::rne) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; 0b0'0001 ; Frm::rtz) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; 0b0'0001 ; Frm::rdn) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; 0b0'0001 ; Frm::rup) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; 0b0'0001 ; Frm::rmm) + $FRtypeInstrCvtF2F (FCVT ; $FSize64 ; $FSize32 ; 0b0'0001 ; Frm::dyn) ) } diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.c b/vadl/main/resources/templates/iss/target/gen-arch/helper.c index b4d42cd24..7eb8788aa 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.c +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.c @@ -36,7 +36,7 @@ void helper_unsupported(CPU[(${gen_arch_upper})]State *env) { // float helpers -void prep_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status *s) { +void prep_float_status(CPU[(${gen_arch_upper})]State *env, float_status *s, uint32_t rm) { uint16_t flags = 0xffff; // un-set non sticky flags [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.non_sticky_fe_flags}"] @@ -45,11 +45,12 @@ void prep_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status [# th:each="reg : ${register_tensors}"][# th:each="flag : ${reg.sticky_fe_flags}"] flags &= ~((1 - ((env->[(${reg.name_lower})] >> [(${flag.idx})]) & 1)) << [(${flag.flag_idx})]);[/][/] set_float_exception_flags(flags, s); + set_float_rounding_mode(rm, s); // TODO: this disables nan-propagation. this will be configurable via the vadl spec at some point set_default_nan_mode(1, s); } -void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status *s) { +void set_float_status(CPU[(${gen_arch_upper})]State *env, float_status *s) { // DEV NOTE: for now we write directly to the flags register. This means that the helper is not pure and // thus slower. In the future, we should optimize this. uint16_t flags = get_float_exception_flags(s); @@ -59,66 +60,73 @@ void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status env->[(${reg.name_lower})] &= ~((1 - (flags >> [(${flag.flag_idx})] & 1)) << [(${flag.idx})]);[/][/] } -#define FLOAT_HELPER_BODY(RET_TY, CALL, FMT) \ +#define FLOAT_HELPER_BODY(RET_TY, CALL, FMT, RM) \ float_status *s = &env->fp_status_##FMT; \ - prep_float_status_fe_flags(env, s); \ + prep_float_status(env, s, RM); \ RET_TY result = CALL; \ - set_float_status_fe_flags(env, s); \ + set_float_status(env, s); \ return result; #define FLOAT_HELPER_1(S, FMT, NAME, QEMU_FUN) \ uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1) { \ - FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, s), FMT) \ + uint##S##_t rs1, uint32_t rm) { \ + FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, s), FMT, rm) \ } #define FLOAT_HELPER_2(S, FMT, NAME, QEMU_FUN) \ + uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ + uint##S##_t rs1, uint##S##_t rs2, uint32_t rm) { \ + FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, rs2, s), FMT, rm) \ + } + +#define FLOAT_HELPER_MINMAX(S, FMT, NAME, QEMU_FUN) \ uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ uint##S##_t rs1, uint##S##_t rs2) { \ - FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, rs2, s), FMT) \ + FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, rs2, s), FMT, 0) \ } #define FLOAT_HELPER_3(S, FMT, NAME, QEMU_FUN, FLAGS) \ uint##S##_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3) { \ - FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, rs2, rs3, FLAGS, s), FMT) \ + uint##S##_t rs1, uint##S##_t rs2, uint##S##_t rs3, \ + uint32_t rm) { \ + FLOAT_HELPER_BODY(uint##S##_t, float##S##_##QEMU_FUN(rs1, rs2, rs3, FLAGS, s), FMT, rm) \ } #define FLOAT_HELPER_F2I(S, FMT, INT_S, INT_FMT, NAME) \ uint##INT_S##_t helper_##FMT##_##INT_S##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1) { \ - FLOAT_HELPER_BODY(uint##INT_S##_t, float##S##_to_##INT_FMT(rs1, s), FMT) \ + uint##S##_t rs1, uint32_t rm) { \ + FLOAT_HELPER_BODY(uint##INT_S##_t, float##S##_to_##INT_FMT(rs1, s), FMT, rm) \ } #define FLOAT_HELPER_I2F(S, FMT, INT_S, INT_FMT, NAME) \ uint##S##_t helper_##FMT##_##INT_S##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##INT_S##_t rs1) { \ - FLOAT_HELPER_BODY(uint##S##_t, INT_FMT##_to_##float##S(rs1, s), FMT) \ + uint##INT_S##_t rs1, uint32_t rm) { \ + FLOAT_HELPER_BODY(uint##S##_t, INT_FMT##_to_##float##S(rs1, s), FMT, rm) \ } #define FLOAT_HELPER_F2F(S, FMT, S2, FMT2, NAME) \ uint##S2##_t helper_##FMT##_##FMT2##_##NAME(CPU[(${gen_arch_upper})]State *env, \ - uint##S##_t rs1) { \ - FLOAT_HELPER_BODY(uint##S2##_t, float##S##_to_##float##S2(rs1, s), FMT) \ + uint##S##_t rs1, uint32_t rm) { \ + FLOAT_HELPER_BODY(uint##S2##_t, float##S##_to_##float##S2(rs1, s), FMT, rm) \ } // TODO: optimize fe flags (maybe prep can be omitted; or flags set to avoid recomputation) #define FLOAT_HELPER_CMP(S, FMT, NAME, QEMU_FUN) \ uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ uint##S##_t rs1, uint##S##_t rs2) { \ - FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1, rs2, s), FMT) \ + FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1, rs2, s), FMT, 0) \ } #define FLOAT_HELPER_CLASSS(S, FMT, NAME, QEMU_FUN) \ uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ uint##S##_t rs1) { \ - FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1, s), FMT) \ + FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1, s), FMT, 0) \ } #define FLOAT_HELPER_CLASS(S, FMT, NAME, QEMU_FUN) \ uint64_t helper_##FMT##_##NAME(CPU[(${gen_arch_upper})]State *env, \ uint##S##_t rs1) { \ - FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1), FMT) \ + FLOAT_HELPER_BODY(bool, float##S##_##QEMU_FUN(rs1), FMT, 0) \ } // TODO: risc-v specifies eq as quiet. other ISAs might want to configure this @@ -132,8 +140,8 @@ void set_float_status_fe_flags(CPU[(${gen_arch_upper})]State *env, float_status [/][# th:each="c : ${float_builtins.fmsub}"]FLOAT_HELPER_3([(${c[0].bit_size})], [(${c[0].name})], fmsub, muladd, float_muladd_negate_c) [/][# th:each="c : ${float_builtins.fnmadd}"]FLOAT_HELPER_3([(${c[0].bit_size})], [(${c[0].name})], fnmadd, muladd, float_muladd_negate_c | float_muladd_negate_product) [/][# th:each="c : ${float_builtins.fnmsub}"]FLOAT_HELPER_3([(${c[0].bit_size})], [(${c[0].name})], fnmsub, muladd, float_muladd_negate_product) -[/][# th:each="c : ${float_builtins.fmin}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fmin, minimum_number) -[/][# th:each="c : ${float_builtins.fmax}"]FLOAT_HELPER_2([(${c[0].bit_size})], [(${c[0].name})], fmax, maximum_number) +[/][# th:each="c : ${float_builtins.fmin}"]FLOAT_HELPER_MINMAX([(${c[0].bit_size})], [(${c[0].name})], fmin, minimum_number) +[/][# th:each="c : ${float_builtins.fmax}"]FLOAT_HELPER_MINMAX([(${c[0].bit_size})], [(${c[0].name})], fmax, maximum_number) [/][# th:each="c : ${float_builtins.flt}"]FLOAT_HELPER_CMP([(${c[0].bit_size})], [(${c[0].name})], flt, lt) [/][# th:each="c : ${float_builtins.fle}"]FLOAT_HELPER_CMP([(${c[0].bit_size})], [(${c[0].name})], fle, le) [/][# th:each="c : ${float_builtins.feq}"]FLOAT_HELPER_CMP([(${c[0].bit_size})], [(${c[0].name})], feq, eq_quiet) diff --git a/vadl/main/resources/templates/iss/target/gen-arch/helper.h b/vadl/main/resources/templates/iss/target/gen-arch/helper.h index e82dfa8b3..6dac6cd96 100644 --- a/vadl/main/resources/templates/iss/target/gen-arch/helper.h +++ b/vadl/main/resources/templates/iss/target/gen-arch/helper.h @@ -13,25 +13,25 @@ DEF_HELPER_1(unsupported, noreturn, env) // float helpers -[# th:each="c : ${float_builtins.fsqrt}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fsqrt, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fadd}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fsub}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fmul}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fmul, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fdiv}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fdiv, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fmadd}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fmadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fmsub}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fmsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fnmadd}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fnmadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fnmsub}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fnmsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})]) +[# th:each="c : ${float_builtins.fsqrt}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fsqrt, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fadd}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fsub}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fmul}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fmul, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fdiv}"]DEF_HELPER_FLAGS_4([(${c[0].name})]_fdiv, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fmadd}"]DEF_HELPER_FLAGS_5([(${c[0].name})]_fmadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fmsub}"]DEF_HELPER_FLAGS_5([(${c[0].name})]_fmsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fnmadd}"]DEF_HELPER_FLAGS_5([(${c[0].name})]_fnmadd, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fnmsub}"]DEF_HELPER_FLAGS_5([(${c[0].name})]_fnmsub, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})], i[(${c[0].bit_size})], i32) [/][# th:each="c : ${float_builtins.fmin}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fmin, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) [/][# th:each="c : ${float_builtins.fmax}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fmax, 0, i[(${c[0].bit_size})], env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) [/][# th:each="c : ${float_builtins.flt}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_flt, 0, i64, env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) [/][# th:each="c : ${float_builtins.fle}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_fle, 0, i64, env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) [/][# th:each="c : ${float_builtins.feq}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_feq, 0, i64, env, i[(${c[0].bit_size})], i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fcvt}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1].name})]_fcvt, 0, i[(${c[1].bit_size})], env, i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fcvtfs}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtfs, 0, i[(${c[1]})], env, i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fcvtfu}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtfu, 0, i[(${c[1]})], env, i[(${c[0].bit_size})]) -[/][# th:each="c : ${float_builtins.fcvtsf}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtsf, 0, i[(${c[0].bit_size})], env, i[(${c[1]})]) -[/][# th:each="c : ${float_builtins.fcvtuf}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_[(${c[1]})]_fcvtuf, 0, i[(${c[0].bit_size})], env, i[(${c[1]})]) +[/][# th:each="c : ${float_builtins.fcvt}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_[(${c[1].name})]_fcvt, 0, i[(${c[1].bit_size})], env, i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fcvtfs}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_[(${c[1]})]_fcvtfs, 0, i[(${c[1]})], env, i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fcvtfu}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_[(${c[1]})]_fcvtfu, 0, i[(${c[1]})], env, i[(${c[0].bit_size})], i32) +[/][# th:each="c : ${float_builtins.fcvtsf}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_[(${c[1]})]_fcvtsf, 0, i[(${c[0].bit_size})], env, i[(${c[1]})], i32) +[/][# th:each="c : ${float_builtins.fcvtuf}"]DEF_HELPER_FLAGS_3([(${c[0].name})]_[(${c[1]})]_fcvtuf, 0, i[(${c[0].bit_size})], env, i[(${c[1]})], i32) [/][# th:each="c : ${float_builtins.fisinf}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fisinf, 0, i64, env, i[(${c[0].bit_size})]) [/][# th:each="c : ${float_builtins.fiszero}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fiszero, 0, i64, env, i[(${c[0].bit_size})]) [/][# th:each="c : ${float_builtins.fisneg}"]DEF_HELPER_FLAGS_2([(${c[0].name})]_fisneg, 0, i64, env, i[(${c[0].bit_size})]) diff --git a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java index b09502af6..7c86179fd 100644 --- a/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java +++ b/vadl/main/vadl/iss/passes/tcg/lowering/TcgOpLoweringPass.java @@ -1259,15 +1259,15 @@ class BuiltInTcgLoweringExecutor { //// Float Arithmetic //// - .set(BuiltInTable.FSQRT, (ctx) -> floatHelperCall(ctx, 1, "fsqrt")) - .set(BuiltInTable.FADD, (ctx) -> floatHelperCall(ctx, 2, "fadd")) - .set(BuiltInTable.FSUB, (ctx) -> floatHelperCall(ctx, 2, "fsub")) - .set(BuiltInTable.FMUL, (ctx) -> floatHelperCall(ctx, 2, "fmul")) - .set(BuiltInTable.FDIV, (ctx) -> floatHelperCall(ctx, 2, "fdiv")) - .set(BuiltInTable.FMADD, (ctx) -> floatHelperCall(ctx, 3, "fmadd")) - .set(BuiltInTable.FMSUB, (ctx) -> floatHelperCall(ctx, 3, "fmsub")) - .set(BuiltInTable.FNMADD, (ctx) -> floatHelperCall(ctx, 3, "fnmadd")) - .set(BuiltInTable.FNMSUB, (ctx) -> floatHelperCall(ctx, 3, "fnmsub")) + .set(BuiltInTable.FSQRT, (ctx) -> floatHelperCall(ctx, 2, "fsqrt")) + .set(BuiltInTable.FADD, (ctx) -> floatHelperCall(ctx, 3, "fadd")) + .set(BuiltInTable.FSUB, (ctx) -> floatHelperCall(ctx, 3, "fsub")) + .set(BuiltInTable.FMUL, (ctx) -> floatHelperCall(ctx, 3, "fmul")) + .set(BuiltInTable.FDIV, (ctx) -> floatHelperCall(ctx, 3, "fdiv")) + .set(BuiltInTable.FMADD, (ctx) -> floatHelperCall(ctx, 4, "fmadd")) + .set(BuiltInTable.FMSUB, (ctx) -> floatHelperCall(ctx, 4, "fmsub")) + .set(BuiltInTable.FNMADD, (ctx) -> floatHelperCall(ctx, 4, "fnmadd")) + .set(BuiltInTable.FNMSUB, (ctx) -> floatHelperCall(ctx, 4, "fnmsub")) .set(BuiltInTable.FMIN, (ctx) -> floatHelperCall(ctx, 2, "fmin")) .set(BuiltInTable.FMAX, (ctx) -> floatHelperCall(ctx, 2, "fmax")) @@ -1279,11 +1279,11 @@ class BuiltInTcgLoweringExecutor { //// Float to Int Conversion //// - .set(BuiltInTable.FCVT, (ctx) -> floatHelperCall(ctx, 1, "fcvt")) - .set(BuiltInTable.FCVTFS, (ctx) -> floatHelperCall(ctx, 1, "fcvtfs")) - .set(BuiltInTable.FCVTFU, (ctx) -> floatHelperCall(ctx, 1, "fcvtfu")) - .set(BuiltInTable.FCVTSF, (ctx) -> floatHelperCall(ctx, 1, "fcvtsf")) - .set(BuiltInTable.FCVTUF, (ctx) -> floatHelperCall(ctx, 1, "fcvtuf")) + .set(BuiltInTable.FCVT, (ctx) -> floatHelperCall(ctx, 2, "fcvt")) + .set(BuiltInTable.FCVTFS, (ctx) -> floatHelperCall(ctx, 2, "fcvtfs")) + .set(BuiltInTable.FCVTFU, (ctx) -> floatHelperCall(ctx, 2, "fcvtfu")) + .set(BuiltInTable.FCVTSF, (ctx) -> floatHelperCall(ctx, 2, "fcvtsf")) + .set(BuiltInTable.FCVTUF, (ctx) -> floatHelperCall(ctx, 2, "fcvtuf")) //// Float Classification //// @@ -1329,8 +1329,9 @@ private static BuiltInResult out(TcgNode... nodes) { /** * Helper method to create a {@link BuiltInResult} from a helper call for a float built-in. * - * @param ctx The built-in lowering context. - * @param argc The number of arguments the float built-in takes. + * @param ctx The built-in lowering context. + * @param argc The number of arguments the float built-in takes. + * @param name The name of the helper call to generate. * @return A {@link BuiltInResult} containing the helper call. */ private static BuiltInResult floatHelperCall(BuiltInTcgLoweringExecutor.Context ctx, int argc, From ccc069a2a9075726bcb35d799e550700705c69fc Mon Sep 17 00:00:00 2001 From: florianmalicky Date: Sun, 2 Aug 2026 21:10:33 +0200 Subject: [PATCH 11/11] wip: Change builtin constant param syntax From `VADL::name<...>()` to `VADL::name::<...>()`. This eases parsing as it does not require backtracking or reinterpretation --- sys/risc-v/rv64fd.vadl | 28 ++++++------- .../main/vadl/ast/AnnotationTable.java | 11 +++++ .../main/vadl/ast/nodes/SymbolExpr.java | 15 +++++-- vadl-frontend/main/vadl/ast/vadl.ATG | 40 +++++++++++-------- .../typechecker/invalidBuiltitinCalls.vadl | 2 +- .../common/IssFloatBuiltinCollectionPass.java | 9 ++--- .../viam/graph/dependency/BuiltInCall.java | 2 +- 7 files changed, 66 insertions(+), 41 deletions(-) diff --git a/sys/risc-v/rv64fd.vadl b/sys/risc-v/rv64fd.vadl index 89760638b..60755c9a8 100644 --- a/sys/risc-v/rv64fd.vadl +++ b/sys/risc-v/rv64fd.vadl @@ -193,7 +193,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr2rm (( AsId($name, $size.suffix, $FrmName($rm)) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; $funct5 ; - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, frm($rm))) + F(rd) := $NaNBox($size ; VADL::$fun::<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, frm($rm))) )) } @@ -201,7 +201,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b0'0101 ; - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty)) + F(rd) := $NaNBox($size ; VADL::$fun::<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty)) )) } @@ -209,7 +209,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr1rm (( AsId($name, $size.suffix, $FrmName($rm)) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b0'1011 ; - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, frm($rm))) + F(rd) := $NaNBox($size ; VADL::$fun::<$size.fTy>(F(rs1) as $size.ty, frm($rm))) ) ; 0b0'0000 ) } @@ -217,7 +217,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { $FRtypeInstr2 (( AsId($name, $size.suffix) ; AsStr($name, ".", $size.suffix) ; $rm ; $size.fmt ; 0b1'0100 ; - X(rd) := VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty) as UIntR // zero extend + X(rd) := VADL::$fun::<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty) as UIntR // zero extend )) } @@ -254,7 +254,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { AsId($name, $size.suffix, $iSize.cvtSuffix, $u.str("U" ; ""), $FrmName($rm)) ; AsStr($name, ".", $size.suffix, ".", $iSize.mvSuffix, $u.str("U" ; "")) ; $rm ; $size.fmt ; 0b1'1010 ; - F(rd) := $NaNBox($size ; VADL::$u.id(fcvtuf ; fcvtsf)<$size.fTy>(X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), frm($rm))) + F(rd) := $NaNBox($size ; VADL::$u.id(fcvtuf ; fcvtsf)::<$size.fTy>(X(rs1) as $u.id($iSize.uTy ; $iSize.sTy), frm($rm))) ) ; $rs2 ) } @@ -264,7 +264,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { AsStr($name, ".", $iSize.mvSuffix, $u.str("U" ; ""), ".", $size.suffix) ; $rm ; $size.fmt ; 0b1'1000 ; // always sign extend the result, even unsigned results - X(rd) := VADL::$u.id(fcvtfu ; fcvtfs)<$size.fTy, $iSize.size>(F(rs1) as $size.ty, frm($rm)) as SIntR + X(rd) := VADL::$u.id(fcvtfu ; fcvtfs)::<$size.fTy, $iSize.size>(F(rs1) as $size.ty, frm($rm)) as SIntR ) ; $rs2 ) } @@ -273,7 +273,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { AsId($name, $size.suffix, $iSize.suffix, $FrmName($rm)) ; AsStr($name, ".", $size.suffix, ".", $iSize.suffix) ; $rm ; $size.fmt ; 0b0'1000 ; - F(rd) := $NaNBox($size ; VADL::fcvt<$iSize.fTy, $size.fTy>(F(rs1) as $iSize.ty, frm($rm))) + F(rd) := $NaNBox($size ; VADL::fcvt::<$iSize.fTy, $size.fTy>(F(rs1) as $iSize.ty, frm($rm))) ) ; $rs2 ) } @@ -282,12 +282,12 @@ instruction set architecture RV64IFD extending RV64IZicsr = { let f = F(rs1) as $size.ty in // TODO: this is from QEMU's risc-v vector_helper.c fclass_s(...) // what predicates should we implement? - let neg = VADL::fisneg<$size.fTy>(f) in - X(rd) := if VADL::fisinf <$size.fTy>(f) then ( if neg then 1 << 0 else 1 << 7 ) else - if VADL::fiszero <$size.fTy>(f) then ( if neg then 1 << 3 else 1 << 4 ) else - if VADL::fisdenorm<$size.fTy>(f) then ( if neg then 1 << 2 else 1 << 5 ) else - if VADL::fissnan <$size.fTy>(f) then 1 << 8 else - if VADL::fisqnan <$size.fTy>(f) then 1 << 9 else + let neg = VADL::fisneg::<$size.fTy>(f) in + X(rd) := if VADL::fisinf ::<$size.fTy>(f) then ( if neg then 1 << 0 else 1 << 7 ) else + if VADL::fiszero ::<$size.fTy>(f) then ( if neg then 1 << 3 else 1 << 4 ) else + if VADL::fisdenorm::<$size.fTy>(f) then ( if neg then 1 << 2 else 1 << 5 ) else + if VADL::fissnan ::<$size.fTy>(f) then 1 << 8 else + if VADL::fisqnan ::<$size.fTy>(f) then 1 << 9 else ( if neg then 1 << 1 else 1 << 6 ) encoding AsId($name, $size.suffix) = {opcode = 0b101'0011, funct3 = 0b001, rs2 = 0b0'0000, fmt = $size.fmt, funct5 = 0b111'00} assembly AsId($name, $size.suffix) = (AsStr($name), ".", AsStr($size.suffix), " ", register(rd), ",", register(rs1)) @@ -313,7 +313,7 @@ instruction set architecture RV64IFD extending RV64IZicsr = { model FR4typeInstr (name : Id, size : FSizeRec, fun : Id, rm : Ex, opcode : Bin) : IsaDefs = { instruction AsId($name, $size.suffix, $FrmName($rm)) : R4type = - F(rd) := $NaNBox($size ; VADL::$fun<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, F(rs3) as $size.ty, frm($rm))) + F(rd) := $NaNBox($size ; VADL::$fun::<$size.fTy>(F(rs1) as $size.ty, F(rs2) as $size.ty, F(rs3) as $size.ty, frm($rm))) encoding AsId($name, $size.suffix, $FrmName($rm)) = {opcode = $opcode, funct3 = $rm, fmt = $size.fmt} assembly AsId($name, $size.suffix, $FrmName($rm)) = (AsStr($name, ".", $size.suffix), " ", register(rd), ",", register(rs1), ",", register(rs2), ",", $FrmName($rm)) } diff --git a/vadl-frontend/main/vadl/ast/AnnotationTable.java b/vadl-frontend/main/vadl/ast/AnnotationTable.java index a169b9ba4..f54794449 100644 --- a/vadl-frontend/main/vadl/ast/AnnotationTable.java +++ b/vadl-frontend/main/vadl/ast/AnnotationTable.java @@ -1166,6 +1166,17 @@ public String usageString() { } } +/** + * An annotation that can be applied to registers of type {@link FormatType}. It can be used + * to reference one format field of the type. The bit-size of the format field must be 1. + * + *

    Usage examples: + *

    + * [ sticky fe flag overflow : ov ]
    + * register reg : Format
    + * format Format : Bits<8> { ov [7], ... }
    + * 
    + */ class FloatFlagAnnotation extends FormatFieldAnnotation { @LazyInit diff --git a/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java b/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java index 7dba9410e..db8d03dd8 100644 --- a/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java +++ b/vadl-frontend/main/vadl/ast/nodes/SymbolExpr.java @@ -22,7 +22,7 @@ import vadl.utils.SourceLocation; /** - * A representation of terms of form {@code MEM<9>} or {@code VADL::fcvts}. + * A representation of terms of form {@code MEM<9>} or {@code VADL::fcvts::}. * These terms always have at least one argument in the pointy brackets. */ @SuppressWarnings("MissingJavadocMethod") @@ -65,16 +65,25 @@ public SyntaxType syntaxType() { @Override public void prettyPrintExpr(int indent, StringBuilder builder, Precedence parentPrec) { path.prettyPrint(indent, builder); - builder.append("<"); + if (symbolArgs.size() > 1) { + builder.append("::"); + } + builder.append("< "); boolean first = true; for (var arg : symbolArgs) { if (!first) { builder.append(", "); } + if (arg instanceof BinaryExpr) { + builder.append("("); + } arg.prettyPrintExpr(0, builder, Precedence.NoPrecedence); + if (arg instanceof BinaryExpr) { + builder.append(")"); + } first = false; } - builder.append(">"); + builder.append(" >"); } @Override diff --git a/vadl-frontend/main/vadl/ast/vadl.ATG b/vadl-frontend/main/vadl/ast/vadl.ATG index aed18cf2b..3ebe0d9fb 100644 --- a/vadl-frontend/main/vadl/ast/vadl.ATG +++ b/vadl-frontend/main/vadl/ast/vadl.ATG @@ -1800,26 +1800,31 @@ A micro architecture definition (#microArchitectureDefinition) is shown in line // Symbol expressions of form "a::b<3>". // Due to the "<"-ambiguity with the less-than operator, this rule can also return a BinaryExpr. // Use the "allowLtOp" parameter to disallow this behavior, e.g. in type literals. - symbolOrBinaryExpression (. expr = DUMMY_EXPR; boolean multipleTerms = false; .) + symbolOrBinaryExpression (. expr = DUMMY_EXPR; .) = IF (isIdentifierToken(la) || isMacroReplacementOfType(this, BasicSyntaxType.ID)) identifierPath [ - IF (la.kind == _SYM_LT) - SYM_LT (. var lessLoc = lastTokenLoc(); .) - term (. var values = new ArrayList(); values.add(term); .) - // problem: here is ambiguity. E.g.: - // id(id, id) - // ^^^^ is this "id < id" and then "," or is it "id" and then ","? - { - SYM_COMMA - term (. values.add(nextTerm); multipleTerms = true; .) - } - [ - IF (!allowLtOp || multipleTerms || la.kind == _SYM_GT) - SYM_GT (. expr = new SymbolExpr(path, values, path.location().join(lastTokenLoc())); .) - ] (. if (expr.equals(DUMMY_EXPR)) expr = new BinaryExpr((Expr) path, new BinOp(Operator.Less, lessLoc), term); .) - ] (. if (expr.equals(DUMMY_EXPR)) expr = (Expr) path; .) - | macroReplacement (. expr = castExpr(this, node); .) + IF (la.kind == _SYM_NAMESPACE || la.kind == _SYM_LT) (. var values = new ArrayList(); .) + ( + IF (la.kind == _SYM_NAMESPACE) + SYM_NAMESPACE + SYM_LT + term (. values.add(firstTerm); .) + { + SYM_COMMA + term (. values.add(term); .) + } + SYM_GT (. expr = new SymbolExpr(path, values, path.location().join(lastTokenLoc())); .) + | + SYM_LT (. var lessLoc = lastTokenLoc(); .) + term (. values.add(term); .) + [ + IF (!allowLtOp || la.kind == _SYM_GT) + SYM_GT (. expr = new SymbolExpr(path, values, path.location().join(lastTokenLoc())); .) + ] (. if (expr.equals(DUMMY_EXPR)) expr = new BinaryExpr((Expr) path, new BinOp(Operator.Less, lessLoc), term); .) + ) + ] (. if (expr.equals(DUMMY_EXPR)) expr = (Expr) path; .) + | macroReplacement (. expr = castExpr(this, node); .) . binaryOperator (. op = null; .) @@ -1931,6 +1936,7 @@ A micro architecture definition (#microArchitectureDefinition) is shown in line identifierPath (. List segments = new ArrayList<>(); .) = identifierOrPlaceholder (. segments.add(id); .) { + IF (la.kind == _SYM_NAMESPACE && scanner.Peek().kind != _SYM_LT) // symbol expression can look like id::path::<>() SYM_NAMESPACE identifierOrPlaceholder (. segments.add(next); .) } (. if (segments.size() == 1) path = id; diff --git a/vadl-frontend/test/resources/frontend-snapshots/typechecker/invalidBuiltitinCalls.vadl b/vadl-frontend/test/resources/frontend-snapshots/typechecker/invalidBuiltitinCalls.vadl index a6df3b0fb..b53590407 100644 --- a/vadl-frontend/test/resources/frontend-snapshots/typechecker/invalidBuiltitinCalls.vadl +++ b/vadl-frontend/test/resources/frontend-snapshots/typechecker/invalidBuiltitinCalls.vadl @@ -28,7 +28,7 @@ function abc(x: Bits<8>) -> Bits<8> = VADL::adds(1, x) // ╭── test/resources/frontend-snapshots/typechecker/invalidBuiltitinCalls.vadl:8:39 // │ // 8 │ function abc(x: Bits<8>) -> Bits<8> = VADL::adds(1, x) -// │ ^^^^^^^^^^^^^^^^ The builtin has the signature `(BitsType, BitsType) -> StructType` but got `Bits<1>, Bits<8>`. +// │ ^^^^^^^^^^^^^^^^ The builtin has the signature `(BitsType, BitsType) -> StructType` but got `(Bits<1>, Bits<8>)`. // │ help: Try casting some of the constant arguments to explicit types. // │ // diff --git a/vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java b/vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java index 8114fb1a8..2fdc17020 100644 --- a/vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java +++ b/vadl/main/vadl/iss/passes/common/IssFloatBuiltinCollectionPass.java @@ -42,7 +42,7 @@ * Analyzes all VIAM behaviors and creates a list of all called float built-ins and * for each a set of occurring configurations. The configuration is usually just the constant * parameters the builtin has been called with (i.e. the parameters in the angle brackets {@code - * VADL::builtin<...>()}). + * VADL::builtin::<...>()}). * *

    For some builtins, additional information is added to the configuration (e.g. for * {@link BuiltInTable#FCVTSF} and {@link BuiltInTable#FCVTUF}, the bit-size of the operand @@ -69,7 +69,6 @@ public PassName getName() { public record Output(Map>> floatBuiltIns) { } - @CheckForNull @Override public Object execute(PassResults passResults, Specification viam) throws IOException { IdentityHashMap>> floatBuiltIns = @@ -86,9 +85,9 @@ private void handleFloatBuiltin(BuiltInCall call, IdentityHashMap>> floatBuiltIns) { // TODO: here we should check if the constant parameters form a supported float built-in config - // e.g. VADL::fcvt(...) is not valid, but this should be checked in the - // frontend. But we may not support 4-bit floats, so VADL::fadd(...) is valid, but - // not supported. + // e.g. VADL::fcvt::(...) is not valid, but this should be checked in the + // frontend. But we may not support 4-bit floats, so VADL::fadd::(...) is valid, + // but not supported. // TODO: currently this treats every float-type declaration as a unique config. but if two use // the same encoding (and relevant settings), we could convert the format to the encoding diff --git a/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java b/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java index 1af5d47e5..d09542f83 100644 --- a/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java +++ b/vadl/main/vadl/viam/graph/dependency/BuiltInCall.java @@ -194,7 +194,7 @@ public void prettyPrint(StringBuilder sb) { } else { sb.append(builtIn.name()); if (!constArgs.isEmpty()) { - sb.append("<"); + sb.append("::<"); for (int i = 0; i < constArgs.size(); i++) { if (i > 0) { sb.append(", ");