From 12df70b125670abb9441d69f72ff4bcaced2a013 Mon Sep 17 00:00:00 2001 From: doushe821 Date: Tue, 10 Mar 2026 20:22:08 +0300 Subject: [PATCH 1/8] [DSL] Finished encoding description TODO: 1. softfloat lib integration 2. Emition 3. Tests That's it --- lib/ADL/builder.rb | 397 ++++++++++++----------- lib/Target/RISC-V/32I.rb | 583 +++++++++++++++++----------------- lib/Target/RISC-V/32ILoops.rb | 373 +++++++++++----------- lib/Target/RISC-V/64F.rb | 391 +++++++++++++++++++++++ lib/Target/RISC-V/encoding.rb | 230 ++++++++------ 5 files changed, 1211 insertions(+), 763 deletions(-) create mode 100644 lib/Target/RISC-V/64F.rb diff --git a/lib/ADL/builder.rb b/lib/ADL/builder.rb index 83d0313..75b5b66 100644 --- a/lib/ADL/builder.rb +++ b/lib/ADL/builder.rb @@ -1,252 +1,267 @@ -require_relative "scope" -require "Utility/type" +require_relative 'scope' +require 'Utility/type' module SimInfra - class IrStmt - attr_reader :name, :oprnds, :attrs - def initialize(name, oprnds, attrs) - @name = name; @oprnds = oprnds; @attrs = attrs; - end + class IrStmt + attr_reader :name, :oprnds, :attrs - def to_h - { - name: @name, - oprnds: @oprnds.map { |o| - if o.class == Var || o.class == Constant - o.to_h - else - o - end - }, - attrs: @attrs, - } - end + def initialize(name, oprnds, attrs) + @name = name + @oprnds = oprnds + @attrs = attrs + end - def self.from_h(h) - IrStmt.new(h[:name], h[:oprnds], h[:attrs]) - end + def to_h + { + name: @name, + oprnds: @oprnds.map do |o| + if [Var, Constant].include?(o.class) + o.to_h + else + o + end + end, + attrs: @attrs + } end + + def self.from_h(h) + IrStmt.new(h[:name], h[:oprnds], h[:attrs]) + end + end end # Basics -module SimInfra - def assert(condition, msg = nil); raise msg if !condition; end +module SimInfra + def assert(condition, msg = nil) + raise msg unless condition + end - @@instructions = [] - @@interface_functions = [] + @@instructions = [] + @@interface_functions = [] - def self.interface_functions - @@interface_functions - end + def self.interface_functions + @@interface_functions + end - class InstructionInfo - attr_accessor :name, :fields, :frmt, :map, :code, :map_code_blocks, :asm_str, :XLEN, :feature - def initialize(name, feature) - @name = name; - @map_code_blocks = {} - @feature = feature - end + class InstructionInfo + attr_accessor :name, :fields, :frmt, :map, :code, :map_code_blocks, :asm_str, :XLEN, :feature - def to_h - { - name: @name, - fields: @fields.map { |f| f.to_h }, - frmt: @frmt, - XLEN: @XLEN, - asm_str: @asm_str, - code: @code.to_h, - map: @map.to_h, - feature: @feature, - } - end + def initialize(name, feature) + @name = name + @map_code_blocks = {} + @feature = feature + end - def self.from_h(h) - info = InstructionInfo.new(h[:name], h[:feature]) - info.fields = h[:fields].map { |f| Field.from_h(f) } - info.frmt = h[:frmt] - info.XLEN = h[:XLEN] - info.asm_str = h[:asm_str] - info.code = Scope.new(nil) - info.code.instance_variable_set(:@tree, h[:code][:tree].map { |s| IrStmt.from_h(s) }) - info.map = Scope.new(nil) - info.map.instance_variable_set(:@tree, h[:map][:tree].map { |s| IrStmt.from_h(s) }) - info - end + def to_h + { + name: @name, + fields: @fields.map { |f| f.to_h }, + frmt: @frmt, + XLEN: @XLEN, + asm_str: @asm_str, + code: @code.to_h, + map: @map.to_h, + feature: @feature + } + end + + def self.from_h(h) + info = InstructionInfo.new(h[:name], h[:feature]) + info.fields = h[:fields].map { |f| Field.from_h(f) } + info.frmt = h[:frmt] + info.XLEN = h[:XLEN] + info.asm_str = h[:asm_str] + info.code = Scope.new(nil) + info.code.instance_variable_set(:@tree, h[:code][:tree].map { |s| IrStmt.from_h(s) }) + info.map = Scope.new(nil) + info.map.instance_variable_set(:@tree, h[:map][:tree].map { |s| IrStmt.from_h(s) }) + info end + end - class InstructionInfoBuilder - include SimInfra + class InstructionInfoBuilder + include SimInfra - def initialize(name, feature) - @info = InstructionInfo.new(name, feature) - @info.code = Scope.new(nil) + def initialize(name, feature) + @info = InstructionInfo.new(name, feature) + @info.code = Scope.new(nil) - @@interface_functions.each do |func| - if !func[:return_types].empty? - @info.code.instance_eval "def #{func[:name]}(*args) + @@interface_functions.each do |func| + if !func[:return_types].empty? + @info.code.instance_eval "def #{func[:name]}(*args) in_s = *args.map { |a| resolve_const(a) } - in_stmt = [tmpvar(#{func[:return_types][0]})] + in_stmt = [tmpvar(#{func[:return_types][0]})] in_stmt.concat(in_s) return stmt :#{func[:name]}, in_stmt end - " - else - @info.code.instance_eval "def #{func[:name]}(*args) + ", __FILE__, __LINE__ - 6 + else + @info.code.instance_eval "def #{func[:name]}(*args) in_s = *args.map { |a| resolve_const(a) } return stmt :#{func[:name]}, in_s end - " - end - end - - @info.map = Scope.new(nil) + ", __FILE__, __LINE__ - 4 end + end - def encoding(frmt, fields, *args) - @info.fields = fields - @info.frmt= frmt - map args - - sum_bits = 0 - for f in fields - sum_bits += Utility.get_type(f.value.type).bitsize - end - @info.XLEN = sum_bits / 8 - @info.code.instance_eval "def xlen(); return #{@info.XLEN.to_s}; end" - end - attr_reader :info + @info.map = Scope.new(nil) end - def Instruction(name, &block) - module_name = caller[0].split('\'')[1].split(':')[1][0..-2] - - bldr = InstructionInfoBuilder.new(name, module_name.to_sym) - bldr.instance_eval &block - @@instructions << bldr.info - nil # only for debugging in IRB + def encoding(frmt, fields, *args) + @info.fields = fields + @info.frmt = frmt + map args + + sum_bits = 0 + for f in fields + sum_bits += Utility.get_type(f.value.type).bitsize + end + @info.XLEN = sum_bits / 8 + @info.code.instance_eval "def xlen(); return #{@info.XLEN}; end", __FILE__, __LINE__ end + attr_reader :info + end - class InterfaceBuilder - include SimInfra + def Instruction(name, &block) + module_name = caller[0].split('\'')[1].split(':')[1][0..-2] - def function(name, output_types = [], input_types = []) - @@interface_functions << {:name => name, :return_types => output_types, :argument_types => input_types} - end - end + bldr = InstructionInfoBuilder.new(name, module_name.to_sym) + bldr.instance_eval(&block) + @@instructions << bldr.info + nil # only for debugging in IRB + end - def Interface(&blck) - bldr = InterfaceBuilder.new() - - bldr.instance_eval &blck + class InterfaceBuilder + include SimInfra + + def function(name, output_types = [], input_types = []) + @@interface_functions << { name: name, return_types: output_types, argument_types: input_types } end + end - class RegisterFileInfo - attr_accessor :name, :regs - def initialize(name) - @name = name; @regs = [] - end + def Interface(&blck) + bldr = InterfaceBuilder.new - def to_h - { - name: @name, - regs: @regs.map(&:to_h), - } - end + bldr.instance_eval(&blck) + end - def self.from_h(h) - rf = new(h[:name]) - rf.regs = h[:regs].map { |r| Register.from_h(r) } - rf - end + class RegisterFileInfo + attr_accessor :name, :regs + + def initialize(name) + @name = name + @regs = [] end - class Register - attr_reader :name, :size, :attrs - def initialize(name, size, attrs) - @name = name; @size = size; @attrs = attrs - end + def to_h + { + name: @name, + regs: @regs.map(&:to_h) + } + end - def to_h - { - name: @name, - size: @size, - attrs: @attrs, - } - end + def self.from_h(h) + rf = new(h[:name]) + rf.regs = h[:regs].map { |r| Register.from_h(r) } + rf + end + end - def self.from_h(h) - new(h[:name], h[:size], h[:attrs]) - end + class Register + attr_reader :name, :size, :attrs + + def initialize(name, size, attrs) + @name = name + @size = size + @attrs = attrs end - @@regfiles = [] - class RegisterFileBuilder - def initialize(name) - @info = RegisterFileInfo.new(name) - @info.regs = [] - end - attr_reader :info + def to_h + { + name: @name, + size: @size, + attrs: @attrs + } end - def RegisterFile(name, &block) - bldr = RegisterFileBuilder.new(name) - bldr.instance_eval &block - @@regfiles << bldr.info - nil + def self.from_h(h) + new(h[:name], h[:size], h[:attrs]) end + end - def RegFiles() - @@regfiles + @@regfiles = [] + class RegisterFileBuilder + def initialize(name) + @info = RegisterFileInfo.new(name) + @info.regs = [] end + attr_reader :info + end + + def RegisterFile(name, &block) + bldr = RegisterFileBuilder.new(name) + bldr.instance_eval(&block) + @@regfiles << bldr.info + nil + end + + def RegFiles + @@regfiles + end end # * generate precise fields module SimInfra - class RegisterFileBuilder - def r32(sym, *args) - @info.regs << Register.new(sym, 32, args[0] ? [args[0]] : []) - end + class RegisterFileBuilder + def r32(sym, *args) + @info.regs << Register.new(sym, 32, args[0] ? [args[0]] : []) + end - def zero() - :zero - end + def f64(sym, *args) + @info.regs << Register.new(sym, 64, args[0] ? [args[0]] : []) + end - def pc() - :pc - end + def zero + :zero end - class InstructionInfoBuilder - def code(&block) - if !@info.map_code_blocks.empty? - @info.fields.each { |f| - @info.map.method(f.value.name, f.value.type) - } - end - @info.map_code_blocks.each do |k, v| - @info.map.instance_eval v[1] - end - @info.map_code_blocks.each do |k, v| - @info.code.method(k, v[0], @info.map.vars[k].regset) - end - for regfile in @@regfiles - for reg in regfile.regs - @info.code.method(reg.name, ('r' + reg.size.to_s).to_sym) - end - end - @info.code.instance_eval &block - end + def pc + :pc + end + end - def map(blocks) - if (!blocks.nil? && !blocks.empty?) - for blck in blocks - @info.map_code_blocks[blck[0]] = [blck[1], blck[2]] - end - end + class InstructionInfoBuilder + def code(&block) + unless @info.map_code_blocks.empty? + @info.fields.each do |f| + @info.map.method(f.value.name, f.value.type) end + end + @info.map_code_blocks.each do |k, v| + @info.map.instance_eval v[1] + end + @info.map_code_blocks.each do |k, v| + @info.code.method(k, v[0], @info.map.vars[k].regset) + end + for regfile in @@regfiles + for reg in regfile.regs + @info.code.method(reg.name, ('r' + reg.size.to_s).to_sym) + end + end + @info.code.instance_eval(&block) + end + + def map(blocks) + return unless !blocks.nil? && !blocks.empty? - def asm(&block) - @info.asm_str = instance_eval &block + for blck in blocks + @info.map_code_blocks[blck[0]] = [blck[1], blck[2]] end end + + def asm(&block) + @info.asm_str = instance_eval(&block) + end + end end diff --git a/lib/Target/RISC-V/32I.rb b/lib/Target/RISC-V/32I.rb index 6a67a7b..bdf88fe 100644 --- a/lib/Target/RISC-V/32I.rb +++ b/lib/Target/RISC-V/32I.rb @@ -1,292 +1,297 @@ -require_relative "encoding" -require_relative "../../ADL/base" -require_relative "../../ADL/builder" +require_relative 'encoding' +require_relative '../../ADL/base' +require_relative '../../ADL/builder' module RV32I - include SimInfra - extend SimInfra - - Interface { - function :sysCall - } - - RegisterFile(:XRegs) { - r32 :x0, zero - r32 :x1 - r32 :x2 - r32 :x3 - r32 :x4 - r32 :x5 - r32 :x6 - r32 :x7 - r32 :x8 - r32 :x9 - r32 :x10 - r32 :x11 - r32 :x12 - r32 :x13 - r32 :x14 - r32 :x15 - r32 :x16 - r32 :x17 - r32 :x18 - r32 :x19 - r32 :x20 - r32 :x21 - r32 :x22 - r32 :x23 - r32 :x24 - r32 :x25 - r32 :x26 - r32 :x27 - r32 :x28 - r32 :x29 - r32 :x30 - r32 :x31 - r32 :pc, pc - } - - Instruction(:lui) { - encoding *format_u(0b0110111) - asm { "lui {rd}, {imm}" } - code { rd[]= imm } - } - - Instruction(:auipc) { - encoding *format_u(0b0010111) - asm { "auipc {rd}, {imm}" } - code { rd[]= imm + pc } - } - - Instruction(:add) { - encoding *format_r(0b0110011, 0b000, 0b0000000) - asm { "add {rd}, {rs1}, {rs2}" } - code { rd[]= rs1.u + rs2.u } - } - - Instruction(:sub) { - encoding *format_r(0b0110011, 0b000, 0b0100000) - asm { "sub {rd}, {rs1}, {rs2}" } - code { rd[]= rs1.u - rs2.u } - } - - Instruction(:sll) { - encoding *format_r(0b0110011, 0b001, 0b0000000) - asm { "sll {rd}, {rs1}, {rs2}" } - code { rd[]= rs1.u << rs2.u } - } - - Instruction(:slt) { - encoding *format_r(0b0110011, 0b010, 0b0000000) - asm { "slt {rd}, {rs1}, {rs2}" } - code { rd[]= (rs1.s < rs2.s).b32 } - } - - Instruction(:sltu) { - encoding *format_r(0b0110011, 0b011, 0b0000000) - asm { "sltu {rd}, {rs1}, {rs2}" } - code { rd[]= (rs1.u < rs2.u).b32 } - } - - Instruction(:xor) { - encoding *format_r(0b0110011, 0b100, 0b0000000) - asm { "xor {rd}, {rs1}, {rs2}" } - code { rd[]= rs1 ^ rs2 } - } - - Instruction(:srl) { - encoding *format_r(0b0110011, 0b101, 0b0000000) - asm { "srl {rd}, {rs1}, {rs2}" } - code { rd[]= rs1.u >> rs2.u } - } - - Instruction(:sra) { - encoding *format_r(0b0110011, 0b101, 0b0100000) - asm { "sra {rd}, {rs1}, {rs2}" } - code { rd[]= rs1.s >> rs2.s } - } - - Instruction(:or) { - encoding *format_r(0b0110011, 0b110, 0b0000000) - asm { "or {rd}, {rs1}, {rs2}" } - code { rd[]= rs1 | rs2 } - } - - Instruction(:and) { - encoding *format_r(0b0110011, 0b111, 0b0000000) - asm { "and {rd}, {rs1}, {rs2}" } - code { rd[]= rs1 & rs2 } - } - - Instruction(:addi) { - encoding *format_i(0b0010011, 0b000) - asm { "addi {rd}, {rs1}, {imm}" } - code { rd[]= rs1 + imm } - } - - Instruction(:slti) { - encoding *format_i(0b0010011, 0b010) - asm { "slti {rd}, {rs1}, {imm}" } - code { rd[]= (rs1.s < imm).b32 } - } - - Instruction(:sltiu) { - encoding *format_i(0b0010011, 0b011) - asm { "sltiu {rd}, {rs1}, {imm}" } - code { rd[]= (rs1.u < imm.u).b32 } - } - - Instruction(:xori) { - encoding *format_i(0b0010011, 0b100) - asm { "xori {rd}, {rs1}, {imm}" } - code { rd[]= rs1 ^ imm } - } - - Instruction(:ori) { - encoding *format_i(0b0010011, 0b110) - asm { "ori {rd}, {rs1}, {imm}" } - code { rd[]= rs1 | imm } - } - - Instruction(:andi) { - encoding *format_i(0b0010011, 0b111) - asm { "andi {rd}, {rs1}, {imm}" } - code { rd[]= rs1 & imm } - } - - Instruction(:slli) { - encoding *format_i_shift(0b0010011, 0b001, 0b00000) - asm { "slli {rd}, {rs1}, {imm}" } - code { rd[]= rs1 << imm } - } - - Instruction(:srli) { - encoding *format_i_shift(0b0010011, 0b101, 0b00000) - asm { "srli {rd}, {rs1}, {imm}" } - code { rd[]= rs1 >> imm } - } - - Instruction(:srai) { - encoding *format_i_shift(0b0010011, 0b101, 0b01000) - asm { "srai {rd}, {rs1}, {imm}" } - code { rd[]= rs1.s >> imm } - } - - Instruction(:beq) { - encoding *format_b(0b1100011, 0b000) - asm { "beq {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 == rs2, pc + imm, pc + xlen)) } - } - - Instruction(:bne) { - encoding *format_b(0b1100011, 0b001) - asm { "bne {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 != rs2, pc + imm, pc + xlen)) } - } - - Instruction(:blt) { - encoding *format_b(0b1100011, 0b100) - asm { "blt {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.s < rs2.s, pc + imm, pc + xlen)) } - } - - Instruction(:bge) { - encoding *format_b(0b1100011, 0b101) - asm { "bge {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.s >= rs2.s, pc + imm, pc + xlen)) } - } - - Instruction(:bltu) { - encoding *format_b(0b1100011, 0b110) - asm { "bltu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u < rs2.u, pc + imm, pc + xlen)) } - } - - Instruction(:bgeu) { - encoding *format_b(0b1100011, 0b111) - asm { "bgeu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u >= rs2.u, pc + imm, pc + xlen)) } - } - - Instruction(:jal) { - encoding *format_j(0b1101111) - asm { "jal {rd}, {imm}" } - code { rd[]= pc + xlen; branch(pc + imm) } - } - - Instruction(:jalr) { - encoding *format_i(0b1100111, 0b000) - asm { "jalr {rd}, {rs1}, {imm}" } - code { - let :t, :b32, pc + xlen - branch((rs1 + imm) & (~1)) - rd[]= t - } - } - - Instruction(:sb) { - encoding *format_s(0b0100011, 0b000) - asm { "sb {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2[7, 0] } - } - - Instruction(:sh) { - encoding *format_s(0b0100011, 0b001) - asm { "sh {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2[15, 0] } - } - - Instruction(:sw) { - encoding *format_s(0b0100011, 0b010) - asm { "sw {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2 } - } - - Instruction(:lb) { - encoding *format_i(0b0000011, 0b000) - asm { "lb {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b8].s32 } - } - - Instruction(:lh) { - encoding *format_i(0b0000011, 0b001) - asm { "lh {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b16].s32 } - } - - Instruction(:lw) { - encoding *format_i(0b0000011, 0b010) - asm { "lw {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b32] } - } - - Instruction(:lbu) { - encoding *format_i(0b0000011, 0b100) - asm { "lbu {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b8].u32 } - } - - Instruction(:lhu) { - encoding *format_i(0b0000011, 0b101) - asm { "lhu {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b16].u32 } - } - - Instruction(:ecall) { - encoding :E, [field(:c, 31, 0, 0b1110011)] - asm { "ecall" } - code { sysCall } - } - - Instruction(:ebreak) { - encoding :E, [field(:c, 31, 0, 0b100000000000001110011)] - asm { "ebreak" } - code { } - } - - Instruction(:fence) { - encoding :E, [field(:c1, 31, 28, 0b0000), field(:c2, 27, 24), field(:c3, 23, 20), field(:c4, 19, 0, 0b00000000000000001111)] - asm { "fence" } - code { } - } + include SimInfra + extend SimInfra + + Interface do + function :sysCall + end + + RegisterFile(:XRegs) do + r32 :x0, zero + r32 :x1 + r32 :x2 + r32 :x3 + r32 :x4 + r32 :x5 + r32 :x6 + r32 :x7 + r32 :x8 + r32 :x9 + r32 :x10 + r32 :x11 + r32 :x12 + r32 :x13 + r32 :x14 + r32 :x15 + r32 :x16 + r32 :x17 + r32 :x18 + r32 :x19 + r32 :x20 + r32 :x21 + r32 :x22 + r32 :x23 + r32 :x24 + r32 :x25 + r32 :x26 + r32 :x27 + r32 :x28 + r32 :x29 + r32 :x30 + r32 :x31 + r32 :pc, pc + end + + Instruction(:lui) do + encoding(*format_u(0b0110111)) + asm { 'lui {rd}, {imm}' } + code { rd[] = imm } + end + + Instruction(:auipc) do + encoding(*format_u(0b0010111)) + asm { 'auipc {rd}, {imm}' } + code { rd[] = imm + pc } + end + + Instruction(:add) do + encoding(*format_r(0b0110011, 0b000, 0b0000000)) + asm { 'add {rd}, {rs1}, {rs2}' } + code { rd[] = rs1.u + rs2.u } + end + + Instruction(:sub) do + encoding(*format_r(0b0110011, 0b000, 0b0100000)) + asm { 'sub {rd}, {rs1}, {rs2}' } + code { rd[] = rs1.u - rs2.u } + end + + Instruction(:sll) do + encoding(*format_r(0b0110011, 0b001, 0b0000000)) + asm { 'sll {rd}, {rs1}, {rs2}' } + code { rd[] = rs1.u << rs2.u } + end + + Instruction(:slt) do + encoding(*format_r(0b0110011, 0b010, 0b0000000)) + asm { 'slt {rd}, {rs1}, {rs2}' } + code { rd[] = (rs1.s < rs2.s).b32 } + end + + Instruction(:sltu) do + encoding(*format_r(0b0110011, 0b011, 0b0000000)) + asm { 'sltu {rd}, {rs1}, {rs2}' } + code { rd[] = (rs1.u < rs2.u).b32 } + end + + Instruction(:xor) do + encoding(*format_r(0b0110011, 0b100, 0b0000000)) + asm { 'xor {rd}, {rs1}, {rs2}' } + code { rd[] = rs1 ^ rs2 } + end + + Instruction(:srl) do + encoding(*format_r(0b0110011, 0b101, 0b0000000)) + asm { 'srl {rd}, {rs1}, {rs2}' } + code { rd[] = rs1.u >> rs2.u } + end + + Instruction(:sra) do + encoding(*format_r(0b0110011, 0b101, 0b0100000)) + asm { 'sra {rd}, {rs1}, {rs2}' } + code { rd[] = rs1.s >> rs2.s } + end + + Instruction(:or) do + encoding(*format_r(0b0110011, 0b110, 0b0000000)) + asm { 'or {rd}, {rs1}, {rs2}' } + code { rd[] = rs1 | rs2 } + end + + Instruction(:and) do + encoding(*format_r(0b0110011, 0b111, 0b0000000)) + asm { 'and {rd}, {rs1}, {rs2}' } + code { rd[] = rs1 & rs2 } + end + + Instruction(:addi) do + encoding(*format_i(0b0010011, 0b000)) + asm { 'addi {rd}, {rs1}, {imm}' } + code { rd[] = rs1 + imm } + end + + Instruction(:slti) do + encoding(*format_i(0b0010011, 0b010)) + asm { 'slti {rd}, {rs1}, {imm}' } + code { rd[] = (rs1.s < imm).b32 } + end + + Instruction(:sltiu) do + encoding(*format_i(0b0010011, 0b011)) + asm { 'sltiu {rd}, {rs1}, {imm}' } + code { rd[] = (rs1.u < imm.u).b32 } + end + + Instruction(:xori) do + encoding(*format_i(0b0010011, 0b100)) + asm { 'xori {rd}, {rs1}, {imm}' } + code { rd[] = rs1 ^ imm } + end + + Instruction(:ori) do + encoding(*format_i(0b0010011, 0b110)) + asm { 'ori {rd}, {rs1}, {imm}' } + code { rd[] = rs1 | imm } + end + + Instruction(:andi) do + encoding(*format_i(0b0010011, 0b111)) + asm { 'andi {rd}, {rs1}, {imm}' } + code { rd[] = rs1 & imm } + end + + Instruction(:slli) do + encoding(*format_i_shift(0b0010011, 0b001, 0b00000)) + asm { 'slli {rd}, {rs1}, {imm}' } + code { rd[] = rs1 << imm } + end + + Instruction(:srli) do + encoding(*format_i_shift(0b0010011, 0b101, 0b00000)) + asm { 'srli {rd}, {rs1}, {imm}' } + code { rd[] = rs1 >> imm } + end + + Instruction(:srai) do + encoding(*format_i_shift(0b0010011, 0b101, 0b01000)) + asm { 'srai {rd}, {rs1}, {imm}' } + code { rd[] = rs1.s >> imm } + end + + Instruction(:beq) do + encoding(*format_b(0b1100011, 0b000)) + asm { 'beq {rs1}, {rs2}, {imm}' } + code { branch(select(rs1 == rs2, pc + imm, pc + xlen)) } + end + + Instruction(:bne) do + encoding(*format_b(0b1100011, 0b001)) + asm { 'bne {rs1}, {rs2}, {imm}' } + code { branch(select(rs1 != rs2, pc + imm, pc + xlen)) } + end + + Instruction(:blt) do + encoding(*format_b(0b1100011, 0b100)) + asm { 'blt {rs1}, {rs2}, {imm}' } + code { branch(select(rs1.s < rs2.s, pc + imm, pc + xlen)) } + end + + Instruction(:bge) do + encoding(*format_b(0b1100011, 0b101)) + asm { 'bge {rs1}, {rs2}, {imm}' } + code { branch(select(rs1.s >= rs2.s, pc + imm, pc + xlen)) } + end + + Instruction(:bltu) do + encoding(*format_b(0b1100011, 0b110)) + asm { 'bltu {rs1}, {rs2}, {imm}' } + code { branch(select(rs1.u < rs2.u, pc + imm, pc + xlen)) } + end + + Instruction(:bgeu) do + encoding(*format_b(0b1100011, 0b111)) + asm { 'bgeu {rs1}, {rs2}, {imm}' } + code { branch(select(rs1.u >= rs2.u, pc + imm, pc + xlen)) } + end + + Instruction(:jal) do + encoding(*format_j(0b1101111)) + asm { 'jal {rd}, {imm}' } + code do + rd[] = pc + xlen + branch(pc + imm) + end + end + + Instruction(:jalr) do + encoding(*format_i(0b1100111, 0b000)) + asm { 'jalr {rd}, {rs1}, {imm}' } + code do + let :t, :b32, pc + xlen + branch((rs1 + imm) & (~1)) + rd[] = t + end + end + + Instruction(:sb) do + encoding(*format_s(0b0100011, 0b000)) + asm { 'sb {rs2}, {imm}({rs1})' } + code { mem[rs1 + imm] = rs2[7, 0] } + end + + Instruction(:sh) do + encoding(*format_s(0b0100011, 0b001)) + asm { 'sh {rs2}, {imm}({rs1})' } + code { mem[rs1 + imm] = rs2[15, 0] } + end + + Instruction(:sw) do + encoding(*format_s(0b0100011, 0b010)) + asm { 'sw {rs2}, {imm}({rs1})' } + code { mem[rs1 + imm] = rs2 } + end + + Instruction(:lb) do + encoding(*format_i(0b0000011, 0b000)) + asm { 'lb {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b8].s32 } + end + + Instruction(:lh) do + encoding(*format_i(0b0000011, 0b001)) + asm { 'lh {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b16].s32 } + end + + Instruction(:lw) do + encoding(*format_i(0b0000011, 0b010)) + asm { 'lw {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b32] } + end + + Instruction(:lbu) do + encoding(*format_i(0b0000011, 0b100)) + asm { 'lbu {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b8].u32 } + end + + Instruction(:lhu) do + encoding(*format_i(0b0000011, 0b101)) + asm { 'lhu {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b16].u32 } + end + + Instruction(:ecall) do + encoding :E, [field(:c, 31, 0, 0b1110011)] + asm { 'ecall' } + code { sysCall } + end + + Instruction(:ebreak) do + encoding :E, [field(:c, 31, 0, 0b100000000000001110011)] + asm { 'ebreak' } + code {} + end + + Instruction(:fence) do + encoding :E, + [field(:c1, 31, 28, 0b0000), field(:c2, 27, 24), field(:c3, 23, 20), + field(:c4, 19, 0, 0b00000000000000001111)] + asm { 'fence' } + code {} + end end diff --git a/lib/Target/RISC-V/32ILoops.rb b/lib/Target/RISC-V/32ILoops.rb index 07b0b06..5a144de 100644 --- a/lib/Target/RISC-V/32ILoops.rb +++ b/lib/Target/RISC-V/32ILoops.rb @@ -1,194 +1,195 @@ -require_relative "encoding" -require_relative "../../Generic/base" -require_relative "../../Generic/builder" +require_relative 'encoding' +require_relative '../../Generic/base' +require_relative '../../Generic/builder' module Ops - - class Add; def self.op(a, b); a.u + b.u; end; end - class Sub; def self.op(a, b); a.u - b.u; end; end - class Sll; def self.op(a, b); a.u << b.u; end; end - class Slt; def self.op(a, b); (a.s < b.s).b; end; end - class Sltu; def self.op(a, b); (a.u < b.u).b; end; end - class Xor; def self.op(a, b); a ^ b; end; end - class Srl; def self.op(a, b); a.u >> b.u; end; end - class Sra; def self.op(a, b); a.s >> b.u; end; end - class Or; def self.op(a, b); a | b; end; end - class And; def self.op(a, b); a & b; end; end - class Load8; def self.op(rs1, imm); mem[rs1 + imm, :b8].s32; end; end - class Load16; def self.op(rs1, imm); mem[rs1 + imm, :b16].s32; end; end - class Load32; def self.op(rs1, imm); mem[rs1 + imm, :b32]; end; end - class Load8U; def self.op(rs1, imm); mem[rs1 + imm, :b8].u32; end; end - class Load16U; def self.op(rs1, imm); mem[rs1 + imm, :b16].u32; end; end - + class Add; def self.op(a, b) = a.u + b.u; end + class Sub; def self.op(a, b) = a.u - b.u; end + class Sll; def self.op(a, b) = a.u.<<(b.u); end + class Slt; def self.op(a, b) = (a.s < b.s).b; end + class Sltu; def self.op(a, b) = (a.u < b.u).b; end + class Xor; def self.op(a, b) = a.^(b); end + class Srl; def self.op(a, b) = a.u.>>(b.u); end + class Sra; def self.op(a, b) = a.s.>>(b.u); end + class Or; def self.op(a, b) = a.|(b); end + class And; def self.op(a, b) = a.&(b); end + class Load8; def self.op(rs1, imm) = mem[rs1 + imm, :b8].s32; end + class Load16; def self.op(rs1, imm) = mem[rs1 + imm, :b16].s32; end + class Load32; def self.op(rs1, imm) = mem.[](rs1 + imm, :b32); end + class Load8U; def self.op(rs1, imm) = mem[rs1 + imm, :b8].u32; end + class Load16U; def self.op(rs1, imm) = mem[rs1 + imm, :b16].u32; end end module RV32I - include SimInfra - extend SimInfra - - RegisterFile(:XRegs) { - r32 :x0, zero - for x in (1..31); r32 "x" + x.to_s; end - r32 :pc - } - - Instruction(:lui) { - encoding *format_u(0b0110111) - asm { "lui {rd}, {imm}" } - code { rd[]= imm } - } - - Instruction(:auipc) { - encoding *format_u(0b0010111) - asm { "auipc {rd}, {imm}" } - code { rd[]= imm + pc } - } - - TABLE_R_FORMAT_INSTRUCTIONS = [ - [:add, format_r(0b0110011, 0b000, 0b0000000), Ops::Add], - [:sub, format_r(0b0110011, 0b000, 0b0100000), Ops::Sub], - [:sll, format_r(0b0110011, 0b001, 0b0000000), Ops::Sll], - [:slt, format_r(0b0110011, 0b010, 0b0000000), Ops::Slt], - [:sltu, format_r(0b0110011, 0b011, 0b0000000), Ops::Sltu], - [:xor, format_r(0b0110011, 0b100, 0b0000000), Ops::Xor], - [:srl, format_r(0b0110011, 0b101, 0b0000000), Ops::Srl], - [:sra, format_r(0b0110011, 0b101, 0b0100000), Ops::Sra], - [:or, format_r(0b0110011, 0b110, 0b0000000), Ops::Or], - [:and, format_r(0b0110011, 0b111, 0b0000000), Ops::And], - ] - - for insn in TABLE_R_FORMAT_INSTRUCTIONS - Instruction(insn[0]) { - encoding *insn[1] - asm { insn[0].to_s + "{rd}, {rs1}, {rs2}" } - code { rd[]= insn[2].op(rs1, rs2) } - } + include SimInfra + extend SimInfra + + RegisterFile(:XRegs) do + r32 :x0, zero + for x in (1..31); r32 'x' + x.to_s; end + r32 :pc + end + + Instruction(:lui) do + encoding(*format_u(0b0110111)) + asm { 'lui {rd}, {imm}' } + code { rd[] = imm } + end + + Instruction(:auipc) do + encoding(*format_u(0b0010111)) + asm { 'auipc {rd}, {imm}' } + code { rd[] = imm + pc } + end + + TABLE_R_FORMAT_INSTRUCTIONS = [ + [:add, format_r(0b0110011, 0b000, 0b0000000), Ops::Add], + [:sub, format_r(0b0110011, 0b000, 0b0100000), Ops::Sub], + [:sll, format_r(0b0110011, 0b001, 0b0000000), Ops::Sll], + [:slt, format_r(0b0110011, 0b010, 0b0000000), Ops::Slt], + [:sltu, format_r(0b0110011, 0b011, 0b0000000), Ops::Sltu], + [:xor, format_r(0b0110011, 0b100, 0b0000000), Ops::Xor], + [:srl, format_r(0b0110011, 0b101, 0b0000000), Ops::Srl], + [:sra, format_r(0b0110011, 0b101, 0b0100000), Ops::Sra], + [:or, format_r(0b0110011, 0b110, 0b0000000), Ops::Or], + [:and, format_r(0b0110011, 0b111, 0b0000000), Ops::And] + ] + + for insn in TABLE_R_FORMAT_INSTRUCTIONS + Instruction(insn[0]) do + encoding(*insn[1]) + asm { insn[0].to_s + '{rd}, {rs1}, {rs2}' } + code { rd[] = insn[2].op(rs1, rs2) } end - - TABLE_I_FORMAT_INSTRUCTIONS = [ - [:addi, format_i(0b0010011, 0b000), Ops::Add], - [:xori, format_i(0b0010011, 0b100), Ops::Xor], - [:ori, format_i(0b0010011, 0b110), Ops::Or], - [:andi, format_i(0b0010011, 0b111), Ops::And], - ] - - for insn in TABLE_I_FORMAT_INSTRUCTIONS - Instruction(insn[0]) { - encoding *insn[1] - asm { insn[0].to_s + "{rd}, {rs1}, {imm}" } - code { rd[]= insn[2].op(rs1, imm) } - } + end + + TABLE_I_FORMAT_INSTRUCTIONS = [ + [:addi, format_i(0b0010011, 0b000), Ops::Add], + [:xori, format_i(0b0010011, 0b100), Ops::Xor], + [:ori, format_i(0b0010011, 0b110), Ops::Or], + [:andi, format_i(0b0010011, 0b111), Ops::And] + ] + + for insn in TABLE_I_FORMAT_INSTRUCTIONS + Instruction(insn[0]) do + encoding(*insn[1]) + asm { insn[0].to_s + '{rd}, {rs1}, {imm}' } + code { rd[] = insn[2].op(rs1, imm) } end - - Instruction(:beq) { - encoding *format_b(0b1100011, 0b000) - asm { "beq {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 == rs2, pc + imm, pc + xlen)) } - } - - Instruction(:bne) { - encoding *format_b(0b1100011, 0b001) - asm { "bne {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 != rs2, pc + imm, pc + xlen)) } - } - - Instruction(:blt) { - encoding *format_b(0b1100011, 0b100) - asm { "blt {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 < rs2, pc + imm, pc + xlen)) } - } - - Instruction(:bge) { - encoding *format_b(0b1100011, 0b101) - asm { "bge {rs1}, {rs2}, {imm}" } - code { branch(select(rs1 > rs2, pc + imm, pc + xlen)) } - } - - Instruction(:bltu) { - encoding *format_b(0b1100011, 0b110) - asm { "bltu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u < rs2.u, pc + imm, pc + xlen)) } - } - - Instruction(:bgeu) { - encoding *format_b(0b1100011, 0b111) - asm { "bgeu {rs1}, {rs2}, {imm}" } - code { branch(select(rs1.u > rs2.u, pc + imm, pc + xlen)) } - } - - Instruction(:jal) { - encoding *format_j(0b1101111) - asm { "jal {rd}, {imm}" } - code { rd[]= pc + xlen; branch(pc + imm) } - } - - Instruction(:jalr) { - encoding *format_i(0b1101111, 0b000) - asm { "jalr {rd}, {rs1}, {imm}" } - code { - let :t, :b32, pc + xlen - branch((rs1 + imm) & (~1)) - rd[]= t - } - } - - Instruction(:sb) { - encoding *format_s(0b0100011, 0b000) - asm { "sb {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2[7, 0] } - } - - Instruction(:sh) { - encoding *format_s(0b0100011, 0b001) - asm { "sh {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2[15, 0] } - } - - Instruction(:sw) { - encoding *format_s(0b0100011, 0b010) - asm { "sw {rs2}, {imm}({rs1})" } - code { mem[rs1 + imm]= rs2 } - } - - Instruction(:lb) { - encoding *format_i(0b0100011, 0b000) - asm { "lb {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b8].s32 } - } - - Instruction(:lh) { - encoding *format_i(0b0100011, 0b001) - asm { "lh {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b16].s32 } - } - - Instruction(:lw) { - encoding *format_i(0b0100011, 0b010) - asm { "lw {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b32] } - } - - Instruction(:lbu) { - encoding *format_i(0b0100011, 0b100) - asm { "lbu {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b8].u32 } - } - - Instruction(:lhu) { - encoding *format_i(0b0100011, 0b101) - asm { "lhu {rd}, {imm}({rs1})" } - code { rd[]= mem[rs1 + imm, :b16].u32 } - } - - Instruction(:ecall) { - encoding :E, [field(:c, 31, 0, 0b1110011)] - asm { "ecall" } - code { } - } - - Instruction(:ebreak) { - encoding :E, [field(:c, 31, 0, 0b100000000000001110011)] - asm { "ebreak" } - code { } - } + end + + Instruction(:beq) do + encoding(*format_b(0b1100011, 0b000)) + asm { 'beq {rs1}, {rs2}, {imm}' } + code { branch(select(rs1 == rs2, pc + imm, pc + xlen)) } + end + + Instruction(:bne) do + encoding(*format_b(0b1100011, 0b001)) + asm { 'bne {rs1}, {rs2}, {imm}' } + code { branch(select(rs1 != rs2, pc + imm, pc + xlen)) } + end + + Instruction(:blt) do + encoding(*format_b(0b1100011, 0b100)) + asm { 'blt {rs1}, {rs2}, {imm}' } + code { branch(select(rs1 < rs2, pc + imm, pc + xlen)) } + end + + Instruction(:bge) do + encoding(*format_b(0b1100011, 0b101)) + asm { 'bge {rs1}, {rs2}, {imm}' } + code { branch(select(rs1 > rs2, pc + imm, pc + xlen)) } + end + + Instruction(:bltu) do + encoding(*format_b(0b1100011, 0b110)) + asm { 'bltu {rs1}, {rs2}, {imm}' } + code { branch(select(rs1.u < rs2.u, pc + imm, pc + xlen)) } + end + + Instruction(:bgeu) do + encoding(*format_b(0b1100011, 0b111)) + asm { 'bgeu {rs1}, {rs2}, {imm}' } + code { branch(select(rs1.u > rs2.u, pc + imm, pc + xlen)) } + end + + Instruction(:jal) do + encoding(*format_j(0b1101111)) + asm { 'jal {rd}, {imm}' } + code do + rd[] = pc + xlen + branch(pc + imm) + end + end + + Instruction(:jalr) do + encoding(*format_i(0b1101111, 0b000)) + asm { 'jalr {rd}, {rs1}, {imm}' } + code do + let :t, :b32, pc + xlen + branch((rs1 + imm) & (~1)) + rd[] = t + end + end + + Instruction(:sb) do + encoding(*format_s(0b0100011, 0b000)) + asm { 'sb {rs2}, {imm}({rs1})' } + code { mem[rs1 + imm] = rs2[7, 0] } + end + + Instruction(:sh) do + encoding(*format_s(0b0100011, 0b001)) + asm { 'sh {rs2}, {imm}({rs1})' } + code { mem[rs1 + imm] = rs2[15, 0] } + end + + Instruction(:sw) do + encoding(*format_s(0b0100011, 0b010)) + asm { 'sw {rs2}, {imm}({rs1})' } + code { mem[rs1 + imm] = rs2 } + end + + Instruction(:lb) do + encoding(*format_i(0b0100011, 0b000)) + asm { 'lb {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b8].s32 } + end + + Instruction(:lh) do + encoding(*format_i(0b0100011, 0b001)) + asm { 'lh {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b16].s32 } + end + + Instruction(:lw) do + encoding(*format_i(0b0100011, 0b010)) + asm { 'lw {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b32] } + end + + Instruction(:lbu) do + encoding(*format_i(0b0100011, 0b100)) + asm { 'lbu {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b8].u32 } + end + + Instruction(:lhu) do + encoding(*format_i(0b0100011, 0b101)) + asm { 'lhu {rd}, {imm}({rs1})' } + code { rd[] = mem[rs1 + imm, :b16].u32 } + end + + Instruction(:ecall) do + encoding :E, [field(:c, 31, 0, 0b1110011)] + asm { 'ecall' } + code {} + end + + Instruction(:ebreak) do + encoding :E, [field(:c, 31, 0, 0b100000000000001110011)] + asm { 'ebreak' } + code {} + end end diff --git a/lib/Target/RISC-V/64F.rb b/lib/Target/RISC-V/64F.rb new file mode 100644 index 0000000..95cb9ab --- /dev/null +++ b/lib/Target/RISC-V/64F.rb @@ -0,0 +1,391 @@ +require_relative 'encoding' +require_relative '../../ADL/base' +require_relative '../../ADL/builder' + +module RV64F + include SimInfra + extend SimInfra + # NOTE: semantics now are just dummies, they will change in future + # Register float regs in regfile + RegisterFile(:FRegs) do + (0..31).each do |i| + send(:f64, :"f#{i}") + end + end + + # Basic arithmetic + Instruction(:fadd_s) do + encoding(*format_r_fp(0b1010011, funct7: 0b0000000)) + asm { 'fadd.s {frd}, {frs1}, {frs2}' } + code do + frd[] = f32_add(frs1, frs2, rm_val) + end + end + + Instruction(:fadd_d) do + encoding(*format_r_fp(0b1010011, funct7: 0b0000001)) + asm { 'fadd.d {frd}, {frs1}, {frs2}' } + code do + frd[] = f64_add(frs1, frs2, rm_val) + end + end + + Instruction(:fsub_s) do + encoding(*format_r_fp(0b1010011, funct7: 0b0000100)) + asm { 'fsub.s {frd}, {frs1}, {frs2}' } + code do + frd[] = f32_sub(frs1, frs2, rm_val) + end + end + + Instruction(:fsub_d) do + encoding(*format_r_fp(0b1010011, funct7: 0b0000101)) + asm { 'fsub.d {frd}, {frs1}, {frs2}' } + code do + frd[] = f64_sub(frs1, frs2, rm_val) + end + end + + Instruction(:fmul_s) do + encoding(*format_r_fp(0b1010011, funct7: 0b0001000)) + asm { 'fmul.s {frd}, {frs1}, {frs2}' } + code do + frd[] = f32_mul(frs1, frs2, rm_val) + end + end + + Instruction(:fmul_d) do + encoding(*format_r_fp(0b1010011, funct7: 0b0001001)) + asm { 'fmul.d {frd}, {frs1}, {frs2}' } + code do + frd[] = f64_mul(frs1, frs2, rm_val) + end + end + + Instruction(:fdiv_s) do + encoding(*format_r_fp(0b1010011, funct7: 0b0001100)) + asm { 'fdiv.s {frd}, {frs1}, {frs2}' } + code do + frd[] = f32_div(frs1, frs2, rm_val) + end + end + + Instruction(:fdiv_d) do + encoding(*format_r_fp(0b1010011, funct7: 0b0001101)) + asm { 'fdiv.d {frd}, {frs1}, {frs2}' } + code do + frd[] = f32_div(frs1, frs2, rm_val) + end + end + + Instruction(:fsqrt_s) do + encoding(*format_r_fp(0b1010011, funct7: 0b0101100)) + asm { 'fsqrt.s {frd}, {frs1}' } + code do + frd[] = f32_sqrt(frs1, rm_val) + end + end + + Instruction(:fsqrt_d) do + encoding(*format_r_fp(0b1010011, funct7: 0b0101101)) + asm { 'fsqrt.d {frd}, {frs1}' } + code do + frd[] = f64_sqrt(frs1, rm_val) + end + end + + # Memory + Instruction(:flw) do + encoding(*format_i(0b0000111, funct3: 0b010)) + asm { 'flw {frd}, {imm}({rs1})' } + code do + frd[] = mem[rs1 + imm, :b32] + end + end + + Instruction(:fsw) do + encoding(*format_s(0b0100111, funct3: 0b010)) + asm { 'fsw {frs2}, {imm}({rs1})' } + code do + mem[s1 + imm] = frs2[31, 0] + end + end + + # Fused Mul/Add + Instruction(:fmadd_s) do + encoding(*format_r4_fp(0b1000011)) + asm { 'fmadd.s {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = f32_mulAdd(frs1, frs2, frs3, rm_val) + end + end + + Instruction(:fmadd_d) do + encoding(*format_r4_fp(0b1000011, funct2: 0b01)) + asm { 'fmadd.d {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = f64_mulAdd(frs1, frs2, frs3, rm_val) + end + end + + Instruction(:fmsub_s) do + encoding(*format_r4_fp(0b1000111)) + asm { 'fmsub.s {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = f32_mulAdd(frs1, frs2, -frs3, rm_val) + end + end + + Instruction(:fmsub_d) do + encoding(*format_r4_fp(0b1000111, funct2: 0b01)) + asm { 'fmsub.d {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = f64_mulAdd(frs1, frs2, -frs3, rm_val) + end + end + + Instruction(:fnmadd_s) do + encoding(*format_r4_fp(0b1001111)) + asm { 'fnmadd.s {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = -f32_mulAdd(frs1, frs2, frs3, rm_val) + end + end + + Instruction(:fnmadd_d) do + encoding(*format_r4_fp(0b1001111, funct2: 0b01)) + asm { 'fnmadd.d {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = -f32_mulAdd(frs1, frs2, frs3, rm_val) + end + end + + Instruction(:fnmsub_s) do + encoding(*format_r4_fp(0b1001011)) + asm { 'fnmsub.s {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = -f32_mulAdd(frs1, frs2, -frs3, rm_val) + end + end + + # Sign injection + Instruction(:fsgnj_s) do + encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b000)) + asm { 'fsgnj.s {frd}, {frs1}, {frs2}' } + code do + frd[] = fsgnj32(frs1, frs2) + end + end + + Instruction(:fsgnj_d) do + encoding(*format_r(0b1010011, funct7: 0b0010001, funct3: 0b000)) + asm { 'fsgnj.d {frd}, {frs1}, {frs2}' } + code do + frd[] = fsgnj64(frs1, frs2) + end + end + + Instruction(:fsgnjn_s) do + encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b001)) + asm { 'fsgnjn.s {frd}, {frs1}, {frs2}' } + code do + frd[] = fsgnjn32(frs1, frs2) + end + end + + Instruction(:fsgnjn_d) do + encoding(*format_r(0b1010011, funct7: 0b0010001, funct3: 0b001)) + asm { 'fsgnjn.d {frd}, {frs1}, {frs2}' } + code do + frd[] = fsgnjn64(frs1, frs2) + end + end + + Instruction(:fsgnjx_s) do + encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b010)) + asm { 'fsgnjx.s {frd}, {frs1}, {frs2}' } + code do + frd[] = fsgnjx32(frs1, frs2) + end + end + + Instruction(:fsgnjx_d) do + encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b010)) + asm { 'fsgnjx.d {frd}, {frs1}, {frs2}' } + code do + frd[] = fsgnjx64(frs1, frs2) + end + end + + # Comparisons + Instruction(:fmin_s) do + encoding(*format_r(0b1010011, funct7: 0b0010100, funct3: 0b000)) + asm { 'fmin.s {frd}, {frs1}, {frs2}' } + code do + frd[] = f32_min(frs1, frs2) + end + end + + Instruction(:fmin_d) do + encoding(*format_r(0b1010011, funct7: 0b0010101, funct3: 0b000)) + asm { 'fmin.d {frd}, {frs1}, {frs2}' } + code do + frd[] = f64_min(frs1, frs2) + end + end + + Instruction(:fmax_s) do + encoding(*format_r_fp(0b1010011, funct7: 0b0010100, funct3: 0b001)) + asm { 'fmax.s {frd}, {frs1}, {frs2}' } + code do + frd[] = f32_max(frs1, frs2) + end + end + + Instruction(:fmax_d) do + encoding(*format_r(0b1010011, funct7: 0b0010101, funct3: 0b001)) + asm { 'fmax.s {frd}, {frs1}, {frs2}' } + code do + frd[] = f64_max(frs1, frs2) + end + end + + Instruction(:feq_s) do + encoding(*format_r(0b1010011, funct7: 0b1010000, funct3: 0b010)) + asm { 'feq.s {rd}, {frs1}, {frs2}' } + code do + rd[] = f32_eq(frs1, frs2) + end + end + + Instruction(:feq_d) do + encoding(*format_r(0b1010011, funct7: 0b1010001, funct3: 0b010)) + asm { 'feq.s {rd}, {frs1}, {frs2}' } + code do + rd[] = f64_eq(frs1, frs2) + end + end + + Instruction(:flt_s) do + encoding(*format_r(0b1010011, funct7: 0b1010000, funct3: 0b001)) + asm { 'flt.s {rd}, {frs1}, {frs2}' } + code do + rd[] = f32_lt(frs1, frs2) + end + end + + Instruction(:flt_d) do + encoding(*format_r(0b1010011, funct7: 0b1010001, funct3: 0b001)) + asm { 'flt.d {rd}, {frs1}, {frs2}' } + code do + rd[] = f64_lt(frs1, frs2) + end + end + + Instruction(:fle_s) do + encoding(*format_r(0b1010011, funct7: 0b1010000, funct3: 0b000)) + asm { 'fle.s {rd}, {frs1}, {frs2}' } + code do + rd[] = f32_le(frs1, frs2) + end + end + + Instruction(:fle_d) do + encoding(*format_r(0b1010011, funct7: 0b1010001, funct3: 0b000)) + asm { 'fle.d {rd}, {frs1}, {frs2}' } + code do + rd[] = f64_le(frs1, frs2) + end + end + + # Conversions + Instruction(:fcvt_w_s) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00000)) + asm { 'fcvt.w.s {rd}, {frs1}' } + code do + rd[] = f32_to_i32(frs1, rm_val) + end + end + + Instruction(:fcvt_wu_s) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00001)) + asm { 'fcvt.wu.s {rd}, {frs1}' } + code do + rd[] = f32_to_u32(frs1, rm_val) + end + end + + Instruction(:fcvt_l_s) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00010)) + asm { 'fcvt.l.s {rd}, {frs1}' } + code do + rd[] = f32_to_i64(frs1, rm_val) + end + end + + Instruction(:fcvt_lu_s) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00011)) + asm { 'fcvt.lu.s {rd}, {frs1}' } + code do + rd[] = f32_to_u64(frs1, rm_val) + end + end + + Instruction(:fcvt_s_w) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00000)) + asm { 'fcvt.s.w {frd}, {rs1}' } + code do + frd[] = i32_to_f32(rs1) + end + end + + Instruction(:fcvt_s_wu) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00001)) + asm { 'fcvt.s.wu {frd}, {rs1}' } + code do + frd[] = u32_to_f32(rs1) + end + end + + Instruction(:fcvt_s_l) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00010)) + asm { 'fcvt.s.l {frd}, {rs1}' } + code do + frd[] = i64_to_f32(rs1) + end + end + + Instruction(:fcvt_s_lu) do + encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00011)) + asm { 'fcvt.s.lu {frd}, {rs1}' } + code do + frd[] = u64_to_f32(rs1) + end + end + + # Classification + Instruction(:fclass_s) do + encoding(*format_r(0b1010011, funct7: 0b1110000, funct3: 0b001)) + asm { 'fclass.s {rd}, {frs1}' } + code do + rd[] = f32_classify(frs1) + end + end + + # Move + Instruction(:fmv_x_w) do + encoding(*format_r_fp(0b1010011, funct7: 0b1110000)) + asm { 'fmv.x.w {rd}, {frs1}' } + code do + rd[] = frs1[31, 0] + end + end + + Instruction(:fmv_w_x) do + encoding(*format_r_fp(0b1010011, funct7: 0b1111000)) + asm { 'fmv.w.x {frd}, {rs1}' } + code do + frd[] = rs1[31, 0] + end + end +end diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index 5ff0310..3e6abb5 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -1,109 +1,145 @@ -require_relative "../../ADL/base" +require_relative '../../ADL/base' module SimInfra - def u_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s << 12" - end + def u_imm(imm) + [imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s << 12"] + end - def i_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s32" - end + def i_imm(imm) + [imm, :s32, "let :#{imm}, [:op], :s32, f_#{imm}.s32"] + end - def is_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, f_imm4_0" - end + def is_imm(imm) + [imm, :s32, "let :#{imm}, [:op], :s32, f_imm4_0"] + end - def j_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, (f_imm20.b21 << 20 | f_imm19_12.b21 << 12 | f_imm11.b21 << 11 | f_imm10_1.b21 << 1).s32" - end + def j_imm(imm) + [imm, :s32, + "let :#{imm}, [:op], :s32, (f_imm20.b21 << 20 | f_imm19_12.b21 << 12 | f_imm11.b21 << 11 | f_imm10_1.b21 << 1).s32"] + end - def b_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, (f_imm12.b13 << 12 | f_imm11.b13 << 11 | f_imm10_5.b13 << 5 | f_imm4_1.b13 << 1).s32" - end + def b_imm(imm) + [imm, :s32, + "let :#{imm}, [:op], :s32, (f_imm12.b13 << 12 | f_imm11.b13 << 11 | f_imm10_5.b13 << 5 | f_imm4_1.b13 << 1).s32"] + end - def s_imm(imm) - return imm, :s32, "let :#{imm}, [:op], :s32, (f_imm11_5.b12 << 5 | f_imm4_0.b12).s32" - end + def s_imm(imm) + [imm, :s32, "let :#{imm}, [:op], :s32, (f_imm11_5.b12 << 5 | f_imm4_0.b12).s32"] + end - def xreg(name) - return name, :r32, "let :#{name}, :XRegs, [:op], :r32, f_#{name}" - end + def xreg(name) + [name, :r32, "let :#{name}, :XRegs, [:op], :r32, f_#{name}"] + end end module SimInfra - def format_u(opcode) - return :U, [ - field(:f_opcode, 6, 0, opcode), - field(:f_rd, 11, 7), - field(:f_imm, 31, 12), - ], xreg(:rd), u_imm(:imm) - end - - def format_r(opcode, funct3, funct7) - return :R, [ - field(:f_opcode, 6, 0, opcode), - field(:f_rd, 11, 7), - field(:f_funct3, 14, 12, funct3), - field(:f_rs1, 19, 15), - field(:f_rs2, 24, 20), - field(:f_funct7, 31, 25, funct7), - ], xreg(:rs2), xreg(:rs1), xreg(:rd) - end - - def format_i(opcode, funct3) - return :I, [ - field(:f_opcode, 6, 0, opcode), - field(:f_rd, 11, 7), - field(:f_funct3, 14, 12, funct3), - field(:f_rs1, 19, 15), - field(:f_imm, 31, 20), - ], i_imm(:imm), xreg(:rs1), xreg(:rd) - end - - def format_i_shift(opcode, func3, sopcode) - return :srai, [ - field(:f_opcode, 6, 0, opcode), - field(:func3, 14, 12, func3), - field(:f_imm4_0, 24, 20), - field(:f_rd, 11, 7), - field(:f_rs1, 19, 15), - field(:f_temp, 26, 25, 0b01), - field(:f_sopcode, 31, 27, sopcode), - ], is_imm(:imm), xreg(:rs1), xreg(:rd) - end - - def format_b(opcode, funct3) - return :B, [ - field(:f_opcode, 6, 0, opcode), - field(:f_funct3, 14, 12, funct3), - field(:f_rs1, 19, 15), - field(:f_rs2, 24, 20), - field(:f_imm4_1, 11, 8), - field(:f_imm10_5, 30, 25), - field(:f_imm11, 7, 7), - field(:f_imm12, 31, 31), - ], b_imm(:imm), xreg(:rs1), xreg(:rs2) - end - - def format_j(opcode) - return :J, [ - field(:f_opcode, 6, 0, opcode), - field(:f_rd, 11, 7), - field(:f_imm20, 31, 31), - field(:f_imm19_12, 19, 12), - field(:f_imm11, 20, 20), - field(:f_imm10_1, 30, 21), - ], j_imm(:imm), xreg(:rd) - end - - def format_s(opcode, func3) - return :S, [ - field(:f_opcode, 6, 0, opcode), - field(:func3, 14, 12, func3), - field(:f_imm4_0, 11, 7), - field(:f_rs1, 19, 15), - field(:f_rs2, 24, 20), - field(:f_imm11_5, 31, 25), - ], s_imm(:imm), xreg(:rs1), xreg(:rs2) - end + def format_u(opcode) + [:U, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_imm, 31, 12) + ], xreg(:rd), u_imm(:imm)] + end + + def format_r(opcode, funct3, funct7) + [:R, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_rs1, 19, 15), + field(:f_rs2, 24, 20), + field(:f_funct7, 31, 25, funct7) + ], xreg(:rs2), xreg(:rs1), xreg(:rd)] + end + + def format_r4_fp(opcode, funct2) + [:R4_FP, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_rm, 14, 12), + field(:f_rs1, 19, 15), + field(:f_rs2, 24, 20), + field(:f_rs3, 31, 27), + field(:f_funct2, 26, 25, funct2) + ], xreg(:rs3), xreg(:rs2), xreg(:rs1), xreg(:rd)] + end + + def format_r_fp(opcode, funct7) + [:R_FP, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_rm, 14, 12), + field(:f_rs1, 19, 15), + field(:f_rs2, 24, 20), + field(:f_funct7, 31, 25, funct7) + ], xreg(:rs2), xreg(:rs1), xreg(:rd)] + end + + def format_r_fp_fcvt(opcode, funct5, funct7) + [:R_FCVT, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_rm, 14, 12), + field(:f_rs1, 19, 15), + field(:f_funct5, 24, 20, funct5), + field(:f_funct7, 31, 25, funct7) + ], xreg(:rs1), xreg(:rd)] + end + + def format_i(opcode, funct3) + [:I, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_rs1, 19, 15), + field(:f_imm, 31, 20) + ], i_imm(:imm), xreg(:rs1), xreg(:rd)] + end + + def format_i_shift(opcode, func3, sopcode) + [:srai, [ + field(:f_opcode, 6, 0, opcode), + field(:func3, 14, 12, func3), + field(:f_imm4_0, 24, 20), + field(:f_rd, 11, 7), + field(:f_rs1, 19, 15), + field(:f_temp, 26, 25, 0b01), + field(:f_sopcode, 31, 27, sopcode) + ], is_imm(:imm), xreg(:rs1), xreg(:rd)] + end + + def format_b(opcode, funct3) + [:B, [ + field(:f_opcode, 6, 0, opcode), + field(:f_funct3, 14, 12, funct3), + field(:f_rs1, 19, 15), + field(:f_rs2, 24, 20), + field(:f_imm4_1, 11, 8), + field(:f_imm10_5, 30, 25), + field(:f_imm11, 7, 7), + field(:f_imm12, 31, 31) + ], b_imm(:imm), xreg(:rs1), xreg(:rs2)] + end + + def format_j(opcode) + [:J, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_imm20, 31, 31), + field(:f_imm19_12, 19, 12), + field(:f_imm11, 20, 20), + field(:f_imm10_1, 30, 21) + ], j_imm(:imm), xreg(:rd)] + end + + def format_s(opcode, func3) + [:S, [ + field(:f_opcode, 6, 0, opcode), + field(:func3, 14, 12, func3), + field(:f_imm4_0, 11, 7), + field(:f_rs1, 19, 15), + field(:f_rs2, 24, 20), + field(:f_imm11_5, 31, 25) + ], s_imm(:imm), xreg(:rs1), xreg(:rs2)] + end end From 9ff0027c1bdd854906ed61be3fc639e1cc99dc8b Mon Sep 17 00:00:00 2001 From: doushe821 Date: Wed, 11 Mar 2026 02:53:13 +0300 Subject: [PATCH 2/8] [DSL] Integrated softfloat Also improved builder and encoding. Also integration of softfloat is questionable and I might have to look for better solution. TODO: 1. Finish builder 2. Implement Emitter. 3. Test. Optional: search for better building method. Current one is ugly, but it might be the optimal one, since softfloat doesn't provide cmake for some reason. --- CMakeLists.txt | 1 + cmake/softfloat.cmake | 46 ++++ lib/ADL/scope.rb | 33 +++ lib/Target/RISC-V/64F.rb | 76 +++--- lib/Target/RISC-V/encoding.rb | 50 +++- lib/ir_gen.rb | 1 + sim_gen/CMakeLists.txt | 2 +- sim_gen/Decoders/decoder.rb | 441 +++++++++++++++++----------------- 8 files changed, 372 insertions(+), 278 deletions(-) create mode 100644 cmake/softfloat.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 85689ce..97b6af6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ include(cmake/Bundler.cmake) include(cmake/CompilerOptions.cmake) include(cmake/CPM.cmake) include(cmake/dependencies.cmake) +include(cmake/softfloat.cmake) add_subdirectory(lib) add_subdirectory(sim_lib) diff --git a/cmake/softfloat.cmake b/cmake/softfloat.cmake new file mode 100644 index 0000000..2debb02 --- /dev/null +++ b/cmake/softfloat.cmake @@ -0,0 +1,46 @@ + +# softfloat: floating point arithmetic library +CPMAddPackage( + NAME softfloat + GITHUB_REPOSITORY ucb-bar/berkeley-softfloat-3 + GIT_TAG master + DOWNLOAD_ONLY YES + EXCLUDE_FROM_ALL True + SYSTEM True) + +# IMPORTANT: since official GitHub doesn't use cmake, integrating purely with +# CPM is impossible. + +# Has to be generaterd to avoid using softfloat's makefiles in order to keep +# everything in one build system +set(SOFTFLOAT_PLATFORM_H ${CMAKE_CURRENT_BINARY_DIR}/platform.h) + +file(WRITE ${SOFTFLOAT_PLATFORM_H} +"#ifndef SOFTFLOAT_PLATFORM_H +#define SOFTFLOAT_PLATFORM_H + +#include +#include + +#endif +") + +file(GLOB SOFTFLOAT_SRC + ${softfloat_SOURCE_DIR}/source/*.c +) + +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*F80*") +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*F128*") +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*bf16*") +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*f16_*") + +add_library(softfloat STATIC ${SOFTFLOAT_SRC}) + +target_include_directories(softfloat PUBLIC + ${softfloat_SOURCE_DIR}/source/include + ${softfloat_SOURCE_DIR}/build/Linux-x86_64-GCC + ${softfloat_SOURCE_DIR}/source/8086 + ${CMAKE_CURRENT_BINARY_DIR} +) + +target_compile_definitions(softfloat PRIVATE SOFTFLOAT_FAST_INT64) diff --git a/lib/ADL/scope.rb b/lib/ADL/scope.rb index 57d4643..1cccbbc 100644 --- a/lib/ADL/scope.rb +++ b/lib/ADL/scope.rb @@ -88,6 +88,39 @@ def and(a, b) = binOp(a, b, :and) def eq(a, b) = binOpWType(a, b, :eq, :b1) def ne(a, b) = binOpWType(a, b, :ne, :b1) + def f32_add(a, b) = stmt(:f32_add, [a, b]) + def f64_add(a, b) = stmt(:f64_add, [a, b]) + def f32_sub(a, b) = stmt(:f32_sub, [a, b]) + def f64_sub(a, b) = stmt(:f64_sub, [a, b]) + def f32_mul(a, b) = stmt(:f32_mul, [a, b]) + def f64_mul(a, b) = stmt(:f64_mul, [a, b]) + def f32_div(a, b) = stmt(:f32_div, [a, b]) + def f64_div(a, b) = stmt(:f64_div, [a, b]) + def f32_sqrt(a) = stmt(:f32_sqrt, [a]) + def f64_sqrt(a) = stmt(:f64_sqrt, [a]) + def f32_mul_add(a, b, c) = stmt(:f32_mulAdd, [a, b, c]) + def f64_mul_add(a, b, c) = stmt(:f64_mulAdd, [a, b, c]) + def f32_min(a, b) = stmt(:f32_min, [a, b]) + def f64_min(a, b) = stmt(:f64_min, [a, b]) + def f32_max(a, b) = stmt(:f32_max, [a, b]) + def f64_max(a, b) = stmt(:f64_max, [a, b]) + def f32_eq(a, b) = stmt(:f32_eq, [a, b]) + def f64_eq(a, b) = stmt(:f64_eq, [a, b]) + def f32_lt(a, b) = stmt(:f32_lt, [a, b]) + def f64_lt(a, b) = stmt(:f64_lt, [a, b]) + def f32_le(a, b) = stmt(:f32_le, [a, b]) + def f64_le(a, b) = stmt(:f64_le, [a, b]) + # Add injections with appropriate names + def f32_to_i32(a) = stmt(:f32_to_i32, [a]) + def f32_to_u32(a) = stmt(:f32_to_u32, [a]) + def f32_to_i64(a) = stmt(:f32_to_i64, [a]) + def f32_to_u64(a) = stmt(:f32_to_u64, [a]) + def i32_to_f32(a) = stmt(:i32_to_f32, [a]) + def u32_to_f32(a) = stmt(:u32_to_f32, [a]) + def i64_to_f32(a) = stmt(:i64_to_f32, [a]) + def u64_to_f32(a) = stmt(:u64_to_f32, [a]) + def classify(a) = stmt(:f32_classify, [a]) + def select(p, a, b) a = resolve_const(a) b = resolve_const(b) diff --git a/lib/Target/RISC-V/64F.rb b/lib/Target/RISC-V/64F.rb index 95cb9ab..012bf3f 100644 --- a/lib/Target/RISC-V/64F.rb +++ b/lib/Target/RISC-V/64F.rb @@ -18,7 +18,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0000000)) asm { 'fadd.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_add(frs1, frs2, rm_val) + frd[] = f32_add(frs1, frs2) end end @@ -26,7 +26,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0000001)) asm { 'fadd.d {frd}, {frs1}, {frs2}' } code do - frd[] = f64_add(frs1, frs2, rm_val) + frd[] = f64_add(frs1, frs2) end end @@ -34,7 +34,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0000100)) asm { 'fsub.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_sub(frs1, frs2, rm_val) + frd[] = f32_sub(frs1, frs2) end end @@ -42,7 +42,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0000101)) asm { 'fsub.d {frd}, {frs1}, {frs2}' } code do - frd[] = f64_sub(frs1, frs2, rm_val) + frd[] = f64_sub(frs1, frs2) end end @@ -50,7 +50,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0001000)) asm { 'fmul.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_mul(frs1, frs2, rm_val) + frd[] = f32_mul(frs1, frs2) end end @@ -58,7 +58,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0001001)) asm { 'fmul.d {frd}, {frs1}, {frs2}' } code do - frd[] = f64_mul(frs1, frs2, rm_val) + frd[] = f64_mul(frs1, frs2) end end @@ -66,7 +66,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0001100)) asm { 'fdiv.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_div(frs1, frs2, rm_val) + frd[] = f32_div(frs1, frs2) end end @@ -74,7 +74,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0001101)) asm { 'fdiv.d {frd}, {frs1}, {frs2}' } code do - frd[] = f32_div(frs1, frs2, rm_val) + frd[] = f32_div(frs1, frs2) end end @@ -82,7 +82,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0101100)) asm { 'fsqrt.s {frd}, {frs1}' } code do - frd[] = f32_sqrt(frs1, rm_val) + frd[] = f32_sqrt(frs1) end end @@ -90,7 +90,7 @@ module RV64F encoding(*format_r_fp(0b1010011, funct7: 0b0101101)) asm { 'fsqrt.d {frd}, {frs1}' } code do - frd[] = f64_sqrt(frs1, rm_val) + frd[] = f64_sqrt(frs1) end end @@ -116,7 +116,7 @@ module RV64F encoding(*format_r4_fp(0b1000011)) asm { 'fmadd.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f32_mulAdd(frs1, frs2, frs3, rm_val) + frd[] = f32_mul_add(frs1, frs2, frs3) end end @@ -124,7 +124,7 @@ module RV64F encoding(*format_r4_fp(0b1000011, funct2: 0b01)) asm { 'fmadd.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f64_mulAdd(frs1, frs2, frs3, rm_val) + frd[] = f64_mul_add(frs1, frs2, frs3) end end @@ -132,7 +132,7 @@ module RV64F encoding(*format_r4_fp(0b1000111)) asm { 'fmsub.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f32_mulAdd(frs1, frs2, -frs3, rm_val) + frd[] = f32_mul_add(frs1, frs2, -frs3) end end @@ -140,7 +140,7 @@ module RV64F encoding(*format_r4_fp(0b1000111, funct2: 0b01)) asm { 'fmsub.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f64_mulAdd(frs1, frs2, -frs3, rm_val) + frd[] = f64_mul_add(frs1, frs2, -frs3) end end @@ -148,7 +148,7 @@ module RV64F encoding(*format_r4_fp(0b1001111)) asm { 'fnmadd.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = -f32_mulAdd(frs1, frs2, frs3, rm_val) + frd[] = -f32_mul_add(frs1, frs2, frs3) end end @@ -156,7 +156,7 @@ module RV64F encoding(*format_r4_fp(0b1001111, funct2: 0b01)) asm { 'fnmadd.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = -f32_mulAdd(frs1, frs2, frs3, rm_val) + frd[] = -f32_mul_add(frs1, frs2, frs3) end end @@ -164,13 +164,13 @@ module RV64F encoding(*format_r4_fp(0b1001011)) asm { 'fnmsub.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = -f32_mulAdd(frs1, frs2, -frs3, rm_val) + frd[] = -f32_mul_add(frs1, frs2, -frs3) end end # Sign injection Instruction(:fsgnj_s) do - encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b000)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b000)) asm { 'fsgnj.s {frd}, {frs1}, {frs2}' } code do frd[] = fsgnj32(frs1, frs2) @@ -178,7 +178,7 @@ module RV64F end Instruction(:fsgnj_d) do - encoding(*format_r(0b1010011, funct7: 0b0010001, funct3: 0b000)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010001, funct3: 0b000)) asm { 'fsgnj.d {frd}, {frs1}, {frs2}' } code do frd[] = fsgnj64(frs1, frs2) @@ -186,7 +186,7 @@ module RV64F end Instruction(:fsgnjn_s) do - encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b001)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b001)) asm { 'fsgnjn.s {frd}, {frs1}, {frs2}' } code do frd[] = fsgnjn32(frs1, frs2) @@ -194,7 +194,7 @@ module RV64F end Instruction(:fsgnjn_d) do - encoding(*format_r(0b1010011, funct7: 0b0010001, funct3: 0b001)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010001, funct3: 0b001)) asm { 'fsgnjn.d {frd}, {frs1}, {frs2}' } code do frd[] = fsgnjn64(frs1, frs2) @@ -202,7 +202,7 @@ module RV64F end Instruction(:fsgnjx_s) do - encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b010)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b010)) asm { 'fsgnjx.s {frd}, {frs1}, {frs2}' } code do frd[] = fsgnjx32(frs1, frs2) @@ -210,7 +210,7 @@ module RV64F end Instruction(:fsgnjx_d) do - encoding(*format_r(0b1010011, funct7: 0b0010000, funct3: 0b010)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b010)) asm { 'fsgnjx.d {frd}, {frs1}, {frs2}' } code do frd[] = fsgnjx64(frs1, frs2) @@ -219,7 +219,7 @@ module RV64F # Comparisons Instruction(:fmin_s) do - encoding(*format_r(0b1010011, funct7: 0b0010100, funct3: 0b000)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010100, funct3: 0b000)) asm { 'fmin.s {frd}, {frs1}, {frs2}' } code do frd[] = f32_min(frs1, frs2) @@ -227,7 +227,7 @@ module RV64F end Instruction(:fmin_d) do - encoding(*format_r(0b1010011, funct7: 0b0010101, funct3: 0b000)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010101, funct3: 0b000)) asm { 'fmin.d {frd}, {frs1}, {frs2}' } code do frd[] = f64_min(frs1, frs2) @@ -243,7 +243,7 @@ module RV64F end Instruction(:fmax_d) do - encoding(*format_r(0b1010011, funct7: 0b0010101, funct3: 0b001)) + encoding(*format_r_no_rm(0b1010011, funct7: 0b0010101, funct3: 0b001)) asm { 'fmax.s {frd}, {frs1}, {frs2}' } code do frd[] = f64_max(frs1, frs2) @@ -251,7 +251,7 @@ module RV64F end Instruction(:feq_s) do - encoding(*format_r(0b1010011, funct7: 0b1010000, funct3: 0b010)) + encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010000, funct3: 0b010)) asm { 'feq.s {rd}, {frs1}, {frs2}' } code do rd[] = f32_eq(frs1, frs2) @@ -259,7 +259,7 @@ module RV64F end Instruction(:feq_d) do - encoding(*format_r(0b1010011, funct7: 0b1010001, funct3: 0b010)) + encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010001, funct3: 0b010)) asm { 'feq.s {rd}, {frs1}, {frs2}' } code do rd[] = f64_eq(frs1, frs2) @@ -267,7 +267,7 @@ module RV64F end Instruction(:flt_s) do - encoding(*format_r(0b1010011, funct7: 0b1010000, funct3: 0b001)) + encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010000, funct3: 0b001)) asm { 'flt.s {rd}, {frs1}, {frs2}' } code do rd[] = f32_lt(frs1, frs2) @@ -275,7 +275,7 @@ module RV64F end Instruction(:flt_d) do - encoding(*format_r(0b1010011, funct7: 0b1010001, funct3: 0b001)) + encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010001, funct3: 0b001)) asm { 'flt.d {rd}, {frs1}, {frs2}' } code do rd[] = f64_lt(frs1, frs2) @@ -283,7 +283,7 @@ module RV64F end Instruction(:fle_s) do - encoding(*format_r(0b1010011, funct7: 0b1010000, funct3: 0b000)) + encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010000, funct3: 0b000)) asm { 'fle.s {rd}, {frs1}, {frs2}' } code do rd[] = f32_le(frs1, frs2) @@ -291,7 +291,7 @@ module RV64F end Instruction(:fle_d) do - encoding(*format_r(0b1010011, funct7: 0b1010001, funct3: 0b000)) + encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010001, funct3: 0b000)) asm { 'fle.d {rd}, {frs1}, {frs2}' } code do rd[] = f64_le(frs1, frs2) @@ -303,7 +303,7 @@ module RV64F encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00000)) asm { 'fcvt.w.s {rd}, {frs1}' } code do - rd[] = f32_to_i32(frs1, rm_val) + rd[] = f32_to_i32(frs1) end end @@ -311,7 +311,7 @@ module RV64F encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00001)) asm { 'fcvt.wu.s {rd}, {frs1}' } code do - rd[] = f32_to_u32(frs1, rm_val) + rd[] = f32_to_u32(frs1) end end @@ -319,7 +319,7 @@ module RV64F encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00010)) asm { 'fcvt.l.s {rd}, {frs1}' } code do - rd[] = f32_to_i64(frs1, rm_val) + rd[] = f32_to_i64(frs1) end end @@ -327,7 +327,7 @@ module RV64F encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00011)) asm { 'fcvt.lu.s {rd}, {frs1}' } code do - rd[] = f32_to_u64(frs1, rm_val) + rd[] = f32_to_u64(frs1) end end @@ -364,8 +364,8 @@ module RV64F end # Classification - Instruction(:fclass_s) do - encoding(*format_r(0b1010011, funct7: 0b1110000, funct3: 0b001)) + Instruction(:fclass_s) do # might need separate format + encoding(*format_r_fp_comp(0b1010011, funct7: 0b1110000, funct3: 0b001)) asm { 'fclass.s {rd}, {frs1}' } code do rd[] = f32_classify(frs1) diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index 3e6abb5..5dcd61a 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -30,6 +30,10 @@ def s_imm(imm) def xreg(name) [name, :r32, "let :#{name}, :XRegs, [:op], :r32, f_#{name}"] end + + def freg(name) + [name, :f64, "let :#{name}, :FRegs, [:op], :f64, f_#{name}"] + end end module SimInfra @@ -55,35 +59,57 @@ def format_r(opcode, funct3, funct7) def format_r4_fp(opcode, funct2) [:R4_FP, [ field(:f_opcode, 6, 0, opcode), - field(:f_rd, 11, 7), + field(:f_frd, 11, 7), field(:f_rm, 14, 12), - field(:f_rs1, 19, 15), - field(:f_rs2, 24, 20), - field(:f_rs3, 31, 27), + field(:f_frs1, 19, 15), + field(:f_frs2, 24, 20), + field(:f_frs3, 31, 27), field(:f_funct2, 26, 25, funct2) - ], xreg(:rs3), xreg(:rs2), xreg(:rs1), xreg(:rd)] + ], freg(:frs3), freg(:frs2), freg(:frs1), freg(:frd)] end def format_r_fp(opcode, funct7) [:R_FP, [ field(:f_opcode, 6, 0, opcode), - field(:f_rd, 11, 7), + field(:f_frd, 11, 7), field(:f_rm, 14, 12), - field(:f_rs1, 19, 15), - field(:f_rs2, 24, 20), + field(:f_frs1, 19, 15), + field(:f_frs2, 24, 20), field(:f_funct7, 31, 25, funct7) - ], xreg(:rs2), xreg(:rs1), xreg(:rd)] + ], freg(:frs2), freg(:frs1), freg(:frd)] + end + + def format_r_fp_comp(opcode, funct3, funct7) + [:R_FP_COMP, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_frs1, 19, 15), + field(:f_frs2, 24, 20), + field(:f_funct7, 31, 25, funct7) + ], freg(:frs2), freg(:frs1), xreg(:rd)] + end + + def format_r_fp_no_rm(opcode, funct3, funct7) + [:R_FP_NO_RM, [ + field(:f_opcode, 6, 0, opcode), + field(:f_frd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_frs1, 19, 15), + field(:f_frs2, 24, 20), + field(:f_funct7, 31, 25, funct7) + ], freg(:frs2), freg(:frs1), freg(:frd)] end def format_r_fp_fcvt(opcode, funct5, funct7) [:R_FCVT, [ field(:f_opcode, 6, 0, opcode), - field(:f_rd, 11, 7), + field(:f_frd, 11, 7), field(:f_rm, 14, 12), - field(:f_rs1, 19, 15), + field(:f_frs1, 19, 15), field(:f_funct5, 24, 20, funct5), field(:f_funct7, 31, 25, funct7) - ], xreg(:rs1), xreg(:rd)] + ], freg(:frs1), freg(:frd)] end def format_i(opcode, funct3) diff --git a/lib/ir_gen.rb b/lib/ir_gen.rb index cde48e4..f5efac4 100644 --- a/lib/ir_gen.rb +++ b/lib/ir_gen.rb @@ -4,6 +4,7 @@ require 'ADL/base' require 'ADL/builder' require 'Target/RISC-V/32I' +require 'Target/RISC-V/64F' require 'yaml' diff --git a/sim_gen/CMakeLists.txt b/sim_gen/CMakeLists.txt index db5540a..9bea144 100644 --- a/sim_gen/CMakeLists.txt +++ b/sim_gen/CMakeLists.txt @@ -41,6 +41,6 @@ add_executable(sim ${PROTEA_SIMGEN_OUTPUT_SOURCES} ${protea_simlib_SOURCE_DIR}/memory.cc) target_include_directories(sim PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${protea_simlib_SOURCE_DIR}) -target_link_libraries(sim elfio CLI11 fmt) +target_link_libraries(sim elfio CLI11 fmt softfloat) target_compile_features(sim PRIVATE cxx_std_23) install(TARGETS sim) diff --git a/sim_gen/Decoders/decoder.rb b/sim_gen/Decoders/decoder.rb index 4d2689f..d0cf5c1 100644 --- a/sim_gen/Decoders/decoder.rb +++ b/sim_gen/Decoders/decoder.rb @@ -3,257 +3,244 @@ require 'code_gen/cpp_gen' module SimGen - module Decoder - module Helper - module_function - - def calc_insn_mask(insn) - mask = 0 - for field in insn[:fields] - if !field[:value][:value_num].nil? - field_mask = ((1 << (field[:from] - field[:to] + 1)) - 1) << field[:to] - mask |= field_mask - end - end - mask + module Decoder + module Helper + module_function + + def calc_insn_mask(insn) + mask = 0 + for field in insn[:fields] + unless field[:value][:value_num].nil? + field_mask = ((1 << (field[:from] - field[:to] + 1)) - 1) << field[:to] + mask |= field_mask + end + end + mask + end + + def calc_insn_value(insn) + value = 0 + for field in insn[:fields] + unless field[:value][:value_num].nil? + field_value = field[:value][:value_num] << field[:to] + value |= field_value + end + end + value + end + + def get_maj_range(lead_bits) + bits = lead_bits.keys.sort + best_range = [bits[0], bits[0]] + best_count = 0 + + for i in 0...bits.size + score = 0 + lsb = bits[i] + for j in i...bits.size + bit = bits[j] + break if bit != lsb + (j - i) + + score += lead_bits[bit].min + if score > best_count + best_count = score + best_range = [lsb, bit] end + end + end + best_range + end - def calc_insn_value(insn) - value = 0 - for field in insn[:fields] - if !field[:value][:value_num].nil? - field_value = field[:value][:value_num] << field[:to] - value |= field_value - end - end - value - end + def get_lead_bits(instructions, separ_mask = 0) + lead_bits = {} + max_len = instructions.map { |insn| insn[:XLEN] * 8 }.max + for bit in 0...max_len + next if separ_mask & (1 << bit) != 0 - def get_maj_range(lead_bits) - bits = lead_bits.keys.sort - best_range = [bits[0], bits[0]] - best_count = 0 - - for i in 0...bits.size - score = 0 - lsb = bits[i] - for j in i...bits.size - bit = bits[j] - if bit != lsb + (j - i) - break - end - - score += lead_bits[bit].min - if score > best_count - best_count = score - best_range = [lsb, bit] - end - end - end - best_range - end - - def get_lead_bits(instructions, separ_mask = 0) - lead_bits = {} - max_len = instructions.map { |insn| insn[:XLEN] * 8 }.max - for bit in 0...max_len - if separ_mask & (1 << bit) != 0 - next - end - - count_0 = 0 - count_1 = 0 - - for insn in instructions - insn_mask = calc_insn_mask(insn) - insn_value = calc_insn_value(insn) - - if insn_mask & (1 << bit) == 0 - next - end - - if (insn_value & (1 << bit)) != 0 - count_1 += 1 - else - count_0 += 1 - end - end - if count_0 > 0 && count_1 > 0 - lead_bits[bit] = [count_0, count_1] - end - end - lead_bits - end + count_0 = 0 + count_1 = 0 - def make_head_tree(instructions) - lead_bits = get_lead_bits(instructions) - lsb, msb = get_maj_range(lead_bits) - width = msb - lsb + 1 - - tree = {} - tree[:range] = [lsb, msb] - tree[:nodes] = {} - - for node_value in 0...(1 << width) - actual_node = node_value << lsb - subtree = {} - - result, is_leaf = make_child_tree(actual_node, ((1 << width) - 1) << lsb, instructions, subtree) - - if is_leaf - tree[:nodes][node_value] = result - elsif !subtree.empty? - tree[:nodes][node_value] = subtree - end - end - tree - end + for insn in instructions + insn_mask = calc_insn_mask(insn) + insn_value = calc_insn_value(insn) - def filter_instructions(instructions, node, separ_mask) - res = [] - for insn in instructions - insn_mask = calc_insn_mask(insn) - insn_value = calc_insn_value(insn) - if (insn_mask & separ_mask) != separ_mask - next - end - if (insn_value & separ_mask) == (node & separ_mask) - res << insn - end - end - res - end + next if insn_mask & (1 << bit) == 0 - def make_child_tree(node_value, separ_mask, instructions, subtree) - sublits = filter_instructions(instructions, node_value, separ_mask) - if sublits.empty? - return nil, false - end - if sublits.size == 1 - return sublits[0], true - end - lead_bits = get_lead_bits(sublits, separ_mask) - lsb, msb = get_maj_range(lead_bits) - width = msb - lsb + 1 - subtree[:range] = [lsb, msb] - subtree[:nodes] = {} - - new_mask = separ_mask | ((1 << width) - 1) << lsb - - for child_value in 0...(1 << width) - actual_node = node_value | child_value << lsb - child_subtree = {} - result, is_leaf = make_child_tree(actual_node, new_mask, sublits, child_subtree) - if is_leaf - subtree[:nodes][child_value] = result - elsif !child_subtree.empty? - subtree[:nodes][child_value] = child_subtree - end - end - return subtree, false + if (insn_value & (1 << bit)) != 0 + count_1 += 1 + else + count_0 += 1 end + end + lead_bits[bit] = [count_0, count_1] if count_0 > 0 && count_1 > 0 + end + lead_bits + end - def map_operands(insn) - operands = {} - cnt = 0 - for node in insn[:map][:tree] - if node[:name] == :new_var && !node[:attrs].nil? && node[:attrs].include?(:op) - operands[node[:oprnds][0][:name]] = "insn.operand#{cnt}" - cnt += 1 - end - end - operands - end + def make_head_tree(instructions) + lead_bits = get_lead_bits(instructions) + lsb, msb = get_maj_range(lead_bits) + width = msb - lsb + 1 - def generate_mapping_fields(insn) - emitter = Utility::GenEmitter.new - for node in insn[:fields] - var_type = Utility::HelperCpp.gen_type(Utility.get_type(node[:value][:type]).bitsize) - emitter.emit_line("#{var_type} #{node[:value][:name]} = slice<#{node[:from]}, #{node[:to]}>(raw_insn);") - end - emitter - end + tree = {} + tree[:range] = [lsb, msb] + tree[:nodes] = {} - def emit_binary_op(emitter, operand_map, op, dest, src1, src2) - var = operand_map[dest[:name]] || dest[:name] - expr1 = operand_map[src1[:name]] || src1[:name] - expr2 = operand_map[src2[:name]] || src2[:name] - expr1 = expr1.nil? ? src1[:value] : expr1 - expr2 = expr2.nil? ? src2[:value] : expr2 - emitter.emit_line("#{var} = #{expr1} #{op} #{expr2};") - end + for node_value in 0...(1 << width) + actual_node = node_value << lsb + subtree = {} - def generate_mapping_body(insn) - emitter = Utility::GenEmitter.new - operand_map = map_operands(insn) - gen = CodeGen::CppGenerator.new(emitter, operand_map) - for node in insn[:map][:tree] - gen.generate_statement(node) - end - emitter - end + result, is_leaf = make_child_tree(actual_node, ((1 << width) - 1) << lsb, instructions, subtree) - def generate_decoder_impl(tree) - emitter = Utility::GenEmitter.new - emitter.emit_line("switch ((raw_insn >> #{tree[:range][0]}) & 0b#{((1 << (tree[:range][1] - tree[:range][0] + 1)) - 1).to_s(2)}) {") - emitter.increase_indent - for node_value, node in tree[:nodes].to_a - emitter.emit_line("case 0b#{node_value.to_s(2)}: {") - emitter.increase_indent - if node.key?(:name) - body_emitter = Helper.generate_mapping_body(node) - - fields_emitter = Helper.generate_mapping_fields(node) - - body_emitter.increase_indent_all emitter.indent_size * emitter.indent_level - fields_emitter.increase_indent_all emitter.indent_size * emitter.indent_level - - emitter.emit_line("// Decoded instruction: #{node[:name]}") - emitter.emit_line("insn.m_opc = Opcode::k#{node[:name].to_s.upcase};") - emitter.concat(fields_emitter) - emitter.concat(body_emitter) - emitter.emit_line("return insn;") - else - rec_emitter = generate_decoder_impl(node) - rec_emitter.increase_indent_all 2 - emitter.concat(rec_emitter) - end - emitter.decrease_indent - emitter.emit_line("}") - end - emitter.decrease_indent - emitter.emit_line("}") - emitter - end + if is_leaf + tree[:nodes][node_value] = result + elsif !subtree.empty? + tree[:nodes][node_value] = subtree + end + end + tree + end + def filter_instructions(instructions, node, separ_mask) + res = [] + for insn in instructions + insn_mask = calc_insn_mask(insn) + insn_value = calc_insn_value(insn) + next if (insn_mask & separ_mask) != separ_mask + + res << insn if (insn_value & separ_mask) == (node & separ_mask) + end + res + end + + def make_child_tree(node_value, separ_mask, instructions, subtree) + sublits = filter_instructions(instructions, node_value, separ_mask) + return nil, false if sublits.empty? + return sublits[0], true if sublits.size == 1 + + lead_bits = get_lead_bits(sublits, separ_mask) + lsb, msb = get_maj_range(lead_bits) + width = msb - lsb + 1 + subtree[:range] = [lsb, msb] + subtree[:nodes] = {} + + new_mask = separ_mask | ((1 << width) - 1) << lsb + + for child_value in 0...(1 << width) + actual_node = node_value | child_value << lsb + child_subtree = {} + result, is_leaf = make_child_tree(actual_node, new_mask, sublits, child_subtree) + if is_leaf + subtree[:nodes][child_value] = result + elsif !child_subtree.empty? + subtree[:nodes][child_value] = child_subtree + end + end + [subtree, false] + end + + def map_operands(insn) + operands = {} + cnt = 0 + for node in insn[:map][:tree] + if node[:name] == :new_var && !node[:attrs].nil? && node[:attrs].include?(:op) + operands[node[:oprnds][0][:name]] = "insn.operand#{cnt}" + cnt += 1 + end + end + operands + end + + def generate_mapping_fields(insn) + emitter = Utility::GenEmitter.new + for node in insn[:fields] + var_type = Utility::HelperCpp.gen_type(Utility.get_type(node[:value][:type]).bitsize) + emitter.emit_line("#{var_type} #{node[:value][:name]} = slice<#{node[:from]}, #{node[:to]}>(raw_insn);") end + emitter + end + + def emit_binary_op(emitter, operand_map, op, dest, src1, src2) + var = operand_map[dest[:name]] || dest[:name] + expr1 = operand_map[src1[:name]] || src1[:name] + expr2 = operand_map[src2[:name]] || src2[:name] + expr1 = expr1.nil? ? src1[:value] : expr1 + expr2 = expr2.nil? ? src2[:value] : expr2 + emitter.emit_line("#{var} = #{expr1} #{op} #{expr2};") + end + + def generate_mapping_body(insn) + emitter = Utility::GenEmitter.new + operand_map = map_operands(insn) + gen = CodeGen::CppGenerator.new(emitter, operand_map) + for node in insn[:map][:tree] + gen.generate_statement(node) + end + emitter + end + + def generate_decoder_impl(tree) + emitter = Utility::GenEmitter.new + emitter.emit_line("switch ((raw_insn >> #{tree[:range][0]}) & 0b#{((1 << (tree[:range][1] - tree[:range][0] + 1)) - 1).to_s(2)}) {") + emitter.increase_indent + for node_value, node in tree[:nodes].to_a + emitter.emit_line("case 0b#{node_value.to_s(2)}: {") + emitter.increase_indent + if node.key?(:name) + body_emitter = Helper.generate_mapping_body(node) + + fields_emitter = Helper.generate_mapping_fields(node) + + body_emitter.increase_indent_all emitter.indent_size * emitter.indent_level + fields_emitter.increase_indent_all emitter.indent_size * emitter.indent_level + + emitter.emit_line("// Decoded instruction: #{node[:name]}") + emitter.emit_line("insn.m_opc = Opcode::k#{node[:name].to_s.upcase};") + emitter.concat(fields_emitter) + emitter.concat(body_emitter) + emitter.emit_line('return insn;') + else + rec_emitter = generate_decoder_impl(node) + rec_emitter.increase_indent_all 2 + emitter.concat(rec_emitter) + end + emitter.decrease_indent + emitter.emit_line('}') + end + emitter.decrease_indent + emitter.emit_line('}') + emitter + end + end + + module Header + module_function - module Header - module_function - def generate_decoder(input_ir) -"#ifndef GENERATED_#{input_ir[:isa_name].upcase}_DECODER_HH_INCLUDED + def generate_decoder(input_ir) + "#ifndef GENERATED_#{input_ir[:isa_name].upcase}_DECODER_HH_INCLUDED #define GENERATED_#{input_ir[:isa_name].upcase}_DECODER_HH_INCLUDED #include \"isa.hh\" #include #include - + namespace prot::decoder { using namespace prot::isa; std::optional decode(const uint32_t raw_insn); } // namespace generated::#{input_ir[:isa_name].downcase}::decoder -#endif // GENERATED_#{input_ir[:isa_name].upcase}_DECODER_HH_INCLUDED" - end - end +#endif // GENERATED_#{input_ir[:isa_name].upcase}_DECODER_HH_INCLUDED" + end + end - module TranslationUnit - module_function - def generate_decoder(input_ir) - tree = Helper::make_head_tree(input_ir[:instructions]) - decoder_impl = Helper::generate_decoder_impl(tree) - decoder_impl.increase_indent_all 2 -"#include \"decoder.hh\" + module TranslationUnit + module_function + + def generate_decoder(input_ir) + tree = Helper.make_head_tree(input_ir[:instructions]) + decoder_impl = Helper.generate_decoder_impl(tree) + decoder_impl.increase_indent_all 2 + "#include \"decoder.hh\" #include namespace { @@ -308,7 +295,7 @@ def generate_decoder(input_ir) } } // namespace generated::#{input_ir[:isa_name].downcase}::decoder " - end - end + end end + end end From de428acdd1fd2221821710136138a9b3bb32df20 Mon Sep 17 00:00:00 2001 From: doushe821 Date: Wed, 11 Mar 2026 22:51:52 +0300 Subject: [PATCH 3/8] [DSL] Naive emition is in progress DONE: 1. Extra encoding formats needed for RV64F instructions. 2. Added some helper functions in scope.rb and cpp_gen.rb 3. Added emition of RV64F instrs. TODO: 1. Need to fix types for floating point temporaries. 2. Write tests. 3. Might need to refactor dsl implementation of this extension. Most methods can be generalized by adding their size as an attribute. It goes even further for Conversion instructions, because they all can be merged into one isntruction with 4 attributes. But this might become tidious in debug phase, so in my opinion, tests first, and then this changes. --- code_gen/cpp_gen.rb | 156 ++++++++++++++++++++++++ lib/ADL/scope.rb | 107 +++++++++++----- lib/ADL/value.rb | 99 ++++++++------- lib/ADL/var.rb | 2 + lib/Target/RISC-V/64F.rb | 154 ++++++++++++++--------- lib/Target/RISC-V/encoding.rb | 58 ++++++++- lib/Utility/type.rb | 76 ++++++------ lib/ir_gen.rb | 4 +- sim_gen/ExecEngines/base_exec_engine.rb | 18 ++- 9 files changed, 492 insertions(+), 182 deletions(-) diff --git a/code_gen/cpp_gen.rb b/code_gen/cpp_gen.rb index 4388b34..df68fca 100644 --- a/code_gen/cpp_gen.rb +++ b/code_gen/cpp_gen.rb @@ -22,6 +22,43 @@ def binary_operation(emitter, operation, op_str) emitter.emit_line("#{dst} = #{src1} #{op_str} #{src2};") end + def emit_fp_binary(opname, operation) + dst = map_operand(operation[:oprnds][0]) + src1 = map_operand(operation[:oprnds][1]) + src2 = map_operand(operation[:oprnds][2]) + + @emitter.emit_line("#{dst} = #{opname}(#{src1}, #{src2});") + end + + def emit_fp_unary(opname, operation) + dst = map_operand(operation[:oprnds][0]) + src = map_operand(operation[:oprnds][1]) + + @emitter.emit_line("#{dst} = #{opname}(#{src});") + end + + def emit_fp_ternary(opname, operation) + dst = map_operand(operation[:oprnds][0]) + src1 = map_operand(operation[:oprnds][1]) + src2 = map_operand(operation[:oprnds][2]) + src3 = map_operand(operation[:oprnds][3]) + + @emitter.emit_line("#{dst} = #{opname}(#{src1}, #{src2}, #{src3});") + end + + def map_n_operands(op, n) + ops = [] + (0...n).each do |i| + ops[i] = map_operand(op[:oprnds][i]) + end + ops + end + + def map_operand(op) + val = @mapping[op[:name]] || op[:name] + val.nil? ? op[:value] : val + end + def self.generate_statement(operation) emitter = Utility::GenEmitter.new CppGenerator.new(emitter, operation[:attrs][:mapping]).generate_statement(operation) @@ -136,6 +173,125 @@ def generate_statement(operation) false_val = @mapping[operation[:oprnds][3][:name]] || operation[:oprnds][3][:name] @emitter.emit_line("#{dst} = #{cond} ? #{true_val} : #{false_val};") + + # Floating point arithmetic binary operations + when :f32_add then emit_fp_binary('f32_add', operation) + when :f64_add then emit_fp_binary('f64_add', operation) + + when :f32_sub then emit_fp_binary('f32_sub', operation) + when :f64_sub then emit_fp_binary('f64_sub', operation) + + when :f32_mul then emit_fp_binary('f32_mul', operation) + when :f64_mul then emit_fp_binary('f64_mul', operation) + + when :f32_div then emit_fp_binary('f32_div', operation) + when :f64_div then emit_fp_binary('f64_div', operation) + # Floating point unary operations + when :f32_sqrt then emit_fp_unary('f32_sqrt', operation) + when :f64_sqrt then emit_fp_unary('f64_sqrt', operation) + # Floating point fused operations + when :f32_mul_add then emit_fp_ternary('f32_mulAdd', operation) + when :f64_mul_add then emit_fp_ternary('f64_mulAdd', operation) + when :f32_mul_sub + dst, src1, src2, src3 = map_n_operands(operation, 4) + @emitter.emit_line("#{dst} = f32_mulAdd(#{src1}, #{src2}, -#{src3});") + when :f64_mul_sub + dst, src1, src2, src3 = map_n_operands(operation, 4) + @emitter.emit_line("#{dst} = f64_mulAdd(#{src1}, #{src2}, -#{src3});") + when :f32_mul_sub_n + dst, src1, src2, src3 = map_n_operands(operation, 4) + @emitter.emit_line("#{dst} = f32_mulAdd(-#{src1}, #{src2}, -#{src3});") + when :f64_mul_sub_n + dst, src1, src2, src3 = map_n_operands(operation, 4) + @emitter.emit_line("#{dst} = f64_mulAdd(-#{src1}, #{src2}, -#{src3});") + # Floating point comparison operations + when :f32_eq then emit_fp_binary('f32_eq', operation) + when :f64_eq then emit_fp_binary('f64_eq', operation) + when :f32_lt then emit_fp_binary('f32_lt', operation) + when :f64_lt then emit_fp_binary('f64_lt', operation) + when :f32_le then emit_fp_binary('f32_le', operation) + when :f64_le then emit_fp_binary('f64_le', operation) + when :f32_min then emit_fp_binary('f32_min', operation) + when :f64_min then emit_fp_binary('f64_min', operation) + when :f32_max then emit_fp_binary('f32_max', operation) + when :f64_max then emit_fp_binary('f64_max', operation) + # Floating point injections + when :f32_sign_injection + dst, src1, src2 = map_n_operands(operation, 3) + @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffff) | (#{src2} & 0x80000000);") + when :f64_sign_injection + dst, src1, src2 = map_n_operands(operation, 3) + @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffffffffffffULL) | (#{src2} & 0x8000000000000000ULL);") + when :f32_sign_injection_n + dst, src1, src2 = map_n_operands(operation, 3) + @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffff) | (~#{src2} & 0x80000000);") + when :f64_sign_injection_n + dst, src1, src2 = map_n_operands(operation, 3) + @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffffffffffffULL) | (~#{src2} & 0x8000000000000000ULL);") + when :f32_sign_xor + dst, src1, src2 = map_n_operands(operation, 3) + @emitter.emit_line("#{dst} = #{src1} ^ (#{src2} & 0x80000000);") + when :f64_sign_xor + dst, src1, src2 = map_n_operands(operation, 3) + @emitter.emit_line("#{dst} = #{src1} ^ (#{src2} & 0x8000000000000000ULL);") + # Floating point conversions + when :f32_to_i32 then emit_fp_unary('f32_to_i32', operation) + when :f32_to_u32 then emit_fp_unary('f32_to_ui32', operation) + when :f32_to_i64 then emit_fp_unary('f32_to_i64', operation) + when :f32_to_u64 then emit_fp_unary('f32_to_ui64', operation) + when :i32_to_f32 then emit_fp_unary('i32_to_f32', operation) + when :u32_to_f32 then emit_fp_unary('ui32_to_f32', operation) + when :i64_to_f32 then emit_fp_unary('i64_to_f32', operation) + when :u64_to_f32 then emit_fp_unary('ui64_to_f32', operation) + # Classification + when :f32_classify + dst, src = map_n_operands(operation, 2) + + @emitter.emit_line('{') + @emitter.emit_line("uint32_t _v = #{src};") + @emitter.emit_line('uint32_t _sign = _v >> 31;') + @emitter.emit_line('uint32_t _exp = (_v >> 23) & 0xFF;') + @emitter.emit_line('uint32_t _frac = _v & 0x7FFFFF;') + @emitter.emit_line('') + @emitter.emit_line('if (_exp == 0xFF) {') + @emitter.emit_line(" if (_frac == 0) { #{dst} = _sign ? (1u << 0) : (1u << 7); }") + @emitter.emit_line(" else if (_frac & (1u << 22)) { #{dst} = (1u << 9); }") + @emitter.emit_line(" else { #{dst} = (1u << 8); }") + @emitter.emit_line('}') + @emitter.emit_line('else if (_exp == 0) {') + @emitter.emit_line(" if (_frac == 0) { #{dst} = _sign ? (1u << 3) : (1u << 4); }") + @emitter.emit_line(" else { #{dst} = _sign ? (1u << 2) : (1u << 5); }") + @emitter.emit_line('}') + @emitter.emit_line('else {') + @emitter.emit_line(" #{dst} = _sign ? (1u << 1) : (1u << 6);") + @emitter.emit_line('}') + @emitter.emit_line('}') + + when :f64_classify + dst, src = map_n_operands(operation, 2) + @emitter.emit_line('{') + @emitter.emit_line("uint64_t v = #{src};") + @emitter.emit_line('uint64_t sign = v >> 63;') + @emitter.emit_line('uint64_t exp = (v >> 52) & 0x7FF;') + @emitter.emit_line('uint64_t frac = v & 0xFFFFFFFFFFFFFULL;') + @emitter.emit_line('uint32_t r = 0;') + @emitter.emit_line('') + @emitter.emit_line('if (exp == 0x7FF) {') + @emitter.emit_line(' if (frac == 0) r = sign ? (1u << 0) : (1u << 7);') + @emitter.emit_line(' else r = (frac & (1ULL << 51)) ? (1u << 9) : (1u << 8);') + @emitter.emit_line('}') + @emitter.emit_line('else if (exp == 0) {') + @emitter.emit_line(' if (frac == 0) r = sign ? (1u << 3) : (1u << 4);') + @emitter.emit_line(' else r = sign ? (1u << 2) : (1u << 5);') + @emitter.emit_line('}') + @emitter.emit_line('else {') + @emitter.emit_line(' r = sign ? (1u << 1) : (1u << 6);') + @emitter.emit_line('}') + @emitter.emit_line('') + @emitter.emit_line("#{dst} = r;") + @emitter.emit_line('}') + + else raise 'Unknown statement type, terminating program' end end end diff --git a/lib/ADL/scope.rb b/lib/ADL/scope.rb index 1cccbbc..8b5fa56 100644 --- a/lib/ADL/scope.rb +++ b/lib/ADL/scope.rb @@ -72,6 +72,32 @@ def binOpWType(a, b, op, t) stmt op, [tmpvar(t), a, b] end + def getOpType(a) + Utility.get_type(a.type).typeof == :r ? ('b' + Utility.get_type(a.type).bitsize.to_s).to_sym : a.type + end + + def unOp(a, op) + unOpWType(a, op, getOpType(a)) + end + + def unOpWType(a, op, t) + a = resolve_const(a) + stmt op, [tmpvar(t), a] + end + + def ternOp(a, b, c, op) + ternOpWType(a, b, c, op, getOpType(a)) + end + + def ternOpWType(a, b, c, op, t) + a = resolve_const(a) + b = resolve_const(b) + c = resolve_const(c) + # TODO: check constant size <= bitsize(var) + # assert(a.type== b.type|| a.type == :iconst || b.type== :iconst) + stmt op, [tmpvar(t), a, b, c] + end + # redefine! add & sub will never be the same def add(a, b) = binOp(a, b, :add) def sub(a, b) = binOp(a, b, :sub) @@ -88,38 +114,55 @@ def and(a, b) = binOp(a, b, :and) def eq(a, b) = binOpWType(a, b, :eq, :b1) def ne(a, b) = binOpWType(a, b, :ne, :b1) - def f32_add(a, b) = stmt(:f32_add, [a, b]) - def f64_add(a, b) = stmt(:f64_add, [a, b]) - def f32_sub(a, b) = stmt(:f32_sub, [a, b]) - def f64_sub(a, b) = stmt(:f64_sub, [a, b]) - def f32_mul(a, b) = stmt(:f32_mul, [a, b]) - def f64_mul(a, b) = stmt(:f64_mul, [a, b]) - def f32_div(a, b) = stmt(:f32_div, [a, b]) - def f64_div(a, b) = stmt(:f64_div, [a, b]) - def f32_sqrt(a) = stmt(:f32_sqrt, [a]) - def f64_sqrt(a) = stmt(:f64_sqrt, [a]) - def f32_mul_add(a, b, c) = stmt(:f32_mulAdd, [a, b, c]) - def f64_mul_add(a, b, c) = stmt(:f64_mulAdd, [a, b, c]) - def f32_min(a, b) = stmt(:f32_min, [a, b]) - def f64_min(a, b) = stmt(:f64_min, [a, b]) - def f32_max(a, b) = stmt(:f32_max, [a, b]) - def f64_max(a, b) = stmt(:f64_max, [a, b]) - def f32_eq(a, b) = stmt(:f32_eq, [a, b]) - def f64_eq(a, b) = stmt(:f64_eq, [a, b]) - def f32_lt(a, b) = stmt(:f32_lt, [a, b]) - def f64_lt(a, b) = stmt(:f64_lt, [a, b]) - def f32_le(a, b) = stmt(:f32_le, [a, b]) - def f64_le(a, b) = stmt(:f64_le, [a, b]) - # Add injections with appropriate names - def f32_to_i32(a) = stmt(:f32_to_i32, [a]) - def f32_to_u32(a) = stmt(:f32_to_u32, [a]) - def f32_to_i64(a) = stmt(:f32_to_i64, [a]) - def f32_to_u64(a) = stmt(:f32_to_u64, [a]) - def i32_to_f32(a) = stmt(:i32_to_f32, [a]) - def u32_to_f32(a) = stmt(:u32_to_f32, [a]) - def i64_to_f32(a) = stmt(:i64_to_f32, [a]) - def u64_to_f32(a) = stmt(:u64_to_f32, [a]) - def classify(a) = stmt(:f32_classify, [a]) + # Floating point operations + def f32_add(a, b) = binOp(a, b, :f32_add) + def f64_add(a, b) = binOp(a, b, :f64_add) + def f32_sub(a, b) = binOp(a, b, :f32_sub) + def f64_sub(a, b) = binOp(a, b, :f64_sub) + def f32_mul(a, b) = binOp(a, b, :f32_mul) + def f64_mul(a, b) = binOp(a, b, :f64_mul) + def f32_div(a, b) = binOp(a, b, :f32_div) + def f64_div(a, b) = binOp(a, b, :f64_div) + + def f32_sqrt(a) = unOp(a, :f32_sqrt) + def f64_sqrt(a) = unOp(a, :f64_sqrt) + + def f32_mul_add(a, b, c) = ternOp(a, b, c, :f32_mul_add) + def f64_mul_add(a, b, c) = ternOp(a, b, c, :f64_mul_add) + def f32_mul_sub(a, b, c) = ternOp(a, b, c, :f32_mul_sub) + def f64_mul_sub(a, b, c) = ternOp(a, b, c, :f64_mul_sub) + def f32_mul_add_n(a, b, c) = ternOp(a, b, c, :f32_mul_sub_n) + def f64_mul_add_n(a, b, c) = ternOp(a, b, c, :f64_mul_sub_n) + def f32_mul_sub_n(a, b, c) = ternOp(a, b, c, :f32_mul_sub_n) + def f64_mul_sub_n(a, b, c) = ternOp(a, b, c, :f64_mul_sub_n) + + def f32_min(a, b) = binOp(a, b, :f32_min) + def f64_min(a, b) = binOp(a, b, :f64_min) + def f32_max(a, b) = binOp(a, b, :f32_max) + def f64_max(a, b) = binOp(a, b, :f64_max) + def f32_eq(a, b) = binOp(a, b, :f32_eq) + def f64_eq(a, b) = binOp(a, b, :f64_eq) + def f32_lt(a, b) = binOp(a, b, :f32_lt) + def f64_lt(a, b) = binOp(a, b, :f64_lt) + def f32_le(a, b) = binOp(a, b, :f32_le) + def f64_le(a, b) = binOp(a, b, :f64_le) + def f32_sign_injection(a, b) = binOp(a, b, :f32_sign_injection) + def f64_sign_injection(a, b) = binOp(a, b, :f64_sign_injection) + def f32_sign_injection_n(a, b) = binOp(a, b, :f32_sign_injection_n) + def f64_sign_injection_n(a, b) = binOp(a, b, :f64_sign_injection_n) + def f32_sign_xor(a, b) = binOp(a, b, :f32_sign_xor) + def f64_sign_xor(a, b) = binOp(a, b, :f64_sign_xor) + + # TODO: unary op + def f32_to_i32(a) = unOp(a, :f32_to_i32) + def f32_to_u32(a) = unOp(a, :f32_to_u32) + def f32_to_i64(a) = unOp(a, :f32_to_i64) + def f32_to_u64(a) = unOp(a, :f32_to_u64) + def i32_to_f32(a) = unOp(a, :i32_to_f32) + def u32_to_f32(a) = unOp(a, :u32_to_f32) + def i64_to_f32(a) = unOp(a, :i64_to_f32) + def u64_to_f32(a) = unOp(a, :u64_to_f32) + def f32_classify(a) = unOp(a, :f32_classify) def select(p, a, b) a = resolve_const(a) diff --git a/lib/ADL/value.rb b/lib/ADL/value.rb index 1a6f397..cb26f7d 100644 --- a/lib/ADL/value.rb +++ b/lib/ADL/value.rb @@ -1,50 +1,59 @@ module SimInfra - # Value class is a super class of Variable or Constant. - class Value - # If value is nil then Value represents a variable. - attr_reader :name, :type, :value - def initialize(name, type, value_num) - @name = name; @type = type; @value = value_num - end - def inspect - if @value.nil? - "#{@type}:#{@name}"; - else - "#{@value.to_s(2)}"; - end - end - - def to_h - { - name: @name, - type: @type, - value: @value, - } - end - - def self.from_h(h) - Value.new(h[:name], h[:type], h[:value]) - end + # Value class is a super class of Variable or Constant. + class Value + # If value is nil then Value represents a variable. + attr_reader :name, :type, :value + + def initialize(name, type, value_num) + @name = name + @type = type + @value = value_num + end + + def inspect + if @value.nil? + "#{@type}:#{@name}" + else + "#{@value.to_s(2)}" + end + end + + def to_h + { + name: @name, + type: @type, + value: @value + } + end + + def self.from_h(h) + Value.new(h[:name], h[:type], h[:value]) + end + end + + class Constant + attr_reader :scope, :name, :type, :value + + def initialize(scope, name, value) + @const = value + @scope = scope + @type = :iconst + @value = value + end + + def let(other) = raise('Assign to constant') + def inspect = "#{@name}:#{@type} (#{@scope.object_id}) {=#{@const}}" + + def to_h + { + name: @name, + type: @type, + value: @value + } end - class Constant - attr_reader :scope, :name, :type, :value - def initialize(scope, name, value); - @const = value; @scope = scope; @type = :iconst; @value = value - end - def let(other); raise "Assign to constant"; end - def inspect; "#{@name}:#{@type} (#{@scope.object_id}) {=#{@const}}"; end - - def to_h - { - name: @name, - type: @type, - value: @value, - } - end - - def self.from_h(h, scope) - Constant.new(scope, h[:name], h[:value]) - end + def self.from_h(h, scope) + Constant.new(scope, h[:name], h[:value]) end + end end diff --git a/lib/ADL/var.rb b/lib/ADL/var.rb index 671f258..9dec20d 100644 --- a/lib/ADL/var.rb +++ b/lib/ADL/var.rb @@ -55,6 +55,7 @@ def ==(other) = @scope.eq(self, other) def !=(other) = @scope.ne(self, other) def [](r, l) = @scope.extract(self, r, l) + def f = @scope.cast(self, ('f' + Utility.get_type(@type).bitsize.to_s).to_sym) def u = @scope.cast(self, ('u' + Utility.get_type(@type).bitsize.to_s).to_sym) def s = @scope.cast(self, ('s' + Utility.get_type(@type).bitsize.to_s).to_sym) def b = @scope.cast(self, ('b' + Utility.get_type(@type).bitsize.to_s).to_sym) @@ -62,6 +63,7 @@ def r(regset) = @scope.get_reg(self, regset, ('r' + Utility.get_type(@type).bits def method_missing(name, *regset) if regset.empty? + puts "name = #{name}" instance_eval "def #{name}(); @scope.cast(self, (#{name}).to_sym); end", __FILE__, __LINE__ @scope.cast(self, name.to_sym) else diff --git a/lib/Target/RISC-V/64F.rb b/lib/Target/RISC-V/64F.rb index 012bf3f..629fec3 100644 --- a/lib/Target/RISC-V/64F.rb +++ b/lib/Target/RISC-V/64F.rb @@ -15,7 +15,7 @@ module RV64F # Basic arithmetic Instruction(:fadd_s) do - encoding(*format_r_fp(0b1010011, funct7: 0b0000000)) + encoding(*format_r_fp(0b1010011, 0b0000000)) asm { 'fadd.s {frd}, {frs1}, {frs2}' } code do frd[] = f32_add(frs1, frs2) @@ -23,7 +23,7 @@ module RV64F end Instruction(:fadd_d) do - encoding(*format_r_fp(0b1010011, funct7: 0b0000001)) + encoding(*format_r_fp(0b1010011, 0b0000001)) asm { 'fadd.d {frd}, {frs1}, {frs2}' } code do frd[] = f64_add(frs1, frs2) @@ -31,7 +31,7 @@ module RV64F end Instruction(:fsub_s) do - encoding(*format_r_fp(0b1010011, funct7: 0b0000100)) + encoding(*format_r_fp(0b1010011, 0b0000100)) asm { 'fsub.s {frd}, {frs1}, {frs2}' } code do frd[] = f32_sub(frs1, frs2) @@ -39,7 +39,7 @@ module RV64F end Instruction(:fsub_d) do - encoding(*format_r_fp(0b1010011, funct7: 0b0000101)) + encoding(*format_r_fp(0b1010011, 0b0000101)) asm { 'fsub.d {frd}, {frs1}, {frs2}' } code do frd[] = f64_sub(frs1, frs2) @@ -47,7 +47,7 @@ module RV64F end Instruction(:fmul_s) do - encoding(*format_r_fp(0b1010011, funct7: 0b0001000)) + encoding(*format_r_fp(0b1010011, 0b0001000)) asm { 'fmul.s {frd}, {frs1}, {frs2}' } code do frd[] = f32_mul(frs1, frs2) @@ -55,7 +55,7 @@ module RV64F end Instruction(:fmul_d) do - encoding(*format_r_fp(0b1010011, funct7: 0b0001001)) + encoding(*format_r_fp(0b1010011, 0b0001001)) asm { 'fmul.d {frd}, {frs1}, {frs2}' } code do frd[] = f64_mul(frs1, frs2) @@ -63,7 +63,7 @@ module RV64F end Instruction(:fdiv_s) do - encoding(*format_r_fp(0b1010011, funct7: 0b0001100)) + encoding(*format_r_fp(0b1010011, 0b0001100)) asm { 'fdiv.s {frd}, {frs1}, {frs2}' } code do frd[] = f32_div(frs1, frs2) @@ -71,15 +71,15 @@ module RV64F end Instruction(:fdiv_d) do - encoding(*format_r_fp(0b1010011, funct7: 0b0001101)) + encoding(*format_r_fp(0b1010011, 0b0001101)) asm { 'fdiv.d {frd}, {frs1}, {frs2}' } code do - frd[] = f32_div(frs1, frs2) + frd[] = f64_div(frs1, frs2) end end Instruction(:fsqrt_s) do - encoding(*format_r_fp(0b1010011, funct7: 0b0101100)) + encoding(*format_r_fp(0b1010011, 0b0101100)) asm { 'fsqrt.s {frd}, {frs1}' } code do frd[] = f32_sqrt(frs1) @@ -87,7 +87,7 @@ module RV64F end Instruction(:fsqrt_d) do - encoding(*format_r_fp(0b1010011, funct7: 0b0101101)) + encoding(*format_r_fp(0b1010011, 0b0101101)) asm { 'fsqrt.d {frd}, {frs1}' } code do frd[] = f64_sqrt(frs1) @@ -96,24 +96,40 @@ module RV64F # Memory Instruction(:flw) do - encoding(*format_i(0b0000111, funct3: 0b010)) + encoding(*format_i_fp(0b0000111, 0b010)) asm { 'flw {frd}, {imm}({rs1})' } code do frd[] = mem[rs1 + imm, :b32] end end + Instruction(:fld) do + encoding(*format_i_fp(0b0000111, 0b011)) + asm { 'fld {frd}, {imm}({rs1})' } + code do + frd[] = mem[rs1 + imm, :b64] + end + end + Instruction(:fsw) do - encoding(*format_s(0b0100111, funct3: 0b010)) + encoding(*format_s_fp(0b0100111, 0b010)) asm { 'fsw {frs2}, {imm}({rs1})' } code do - mem[s1 + imm] = frs2[31, 0] + mem[rs1 + imm] = frs2[31, 0] + end + end + + Instruction(:fsd) do + encoding(*format_s_fp(0b0100111, 0b011)) + asm { 'fsd {frs2}, {imm}({rs1})' } + code do + mem[rs1 + imm] = frs2[63, 0] end end # Fused Mul/Add Instruction(:fmadd_s) do - encoding(*format_r4_fp(0b1000011)) + encoding(*format_r4_fp(0b1000011, 0b0)) asm { 'fmadd.s {frd}, {frs1}, {frs2}, {frs3}' } code do frd[] = f32_mul_add(frs1, frs2, frs3) @@ -121,7 +137,7 @@ module RV64F end Instruction(:fmadd_d) do - encoding(*format_r4_fp(0b1000011, funct2: 0b01)) + encoding(*format_r4_fp(0b1000011, 0b01)) asm { 'fmadd.d {frd}, {frs1}, {frs2}, {frs3}' } code do frd[] = f64_mul_add(frs1, frs2, frs3) @@ -129,97 +145,105 @@ module RV64F end Instruction(:fmsub_s) do - encoding(*format_r4_fp(0b1000111)) + encoding(*format_r4_fp(0b1000111, 0b0)) asm { 'fmsub.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f32_mul_add(frs1, frs2, -frs3) + frd[] = f32_mul_sub(frs1, frs2, frs3) end end Instruction(:fmsub_d) do - encoding(*format_r4_fp(0b1000111, funct2: 0b01)) + encoding(*format_r4_fp(0b1000111, 0b01)) asm { 'fmsub.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f64_mul_add(frs1, frs2, -frs3) + frd[] = f64_mul_sub(frs1, frs2, frs3) end end Instruction(:fnmadd_s) do - encoding(*format_r4_fp(0b1001111)) + encoding(*format_r4_fp(0b1001111, 0b00)) asm { 'fnmadd.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = -f32_mul_add(frs1, frs2, frs3) + frd[] = f32_mul_add_n(frs1, frs2, frs3) end end Instruction(:fnmadd_d) do - encoding(*format_r4_fp(0b1001111, funct2: 0b01)) + encoding(*format_r4_fp(0b1001111, 0b01)) asm { 'fnmadd.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = -f32_mul_add(frs1, frs2, frs3) + frd[] = f64_mul_add_n(frs1, frs2, frs3) end end Instruction(:fnmsub_s) do - encoding(*format_r4_fp(0b1001011)) + encoding(*format_r4_fp(0b1001011, 0b00)) asm { 'fnmsub.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = -f32_mul_add(frs1, frs2, -frs3) + frd[] = f32_mul_sub_n(frs1, frs2, frs3) + end + end + + Instruction(:fnmsub_d) do + encoding(*format_r4_fp(0b1001011, 0b01)) + asm { 'fnmsub.d {frd}, {frs1}, {frs2}, {frs3}' } + code do + frd[] = f64_mul_sub_n(frs1, frs2, frs3) end end # Sign injection Instruction(:fsgnj_s) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b000)) + encoding(*format_r_fp_no_rm(0b1010011, 0b000, 0b0010000)) asm { 'fsgnj.s {frd}, {frs1}, {frs2}' } code do - frd[] = fsgnj32(frs1, frs2) + frd[] = f32_sign_injection(frs1, frs2) end end Instruction(:fsgnj_d) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010001, funct3: 0b000)) + encoding(*format_r_fp_no_rm(0b1010011, 0b000, 0b0010001)) asm { 'fsgnj.d {frd}, {frs1}, {frs2}' } code do - frd[] = fsgnj64(frs1, frs2) + frd[] = f64_sign_injection(frs1, frs2) end end Instruction(:fsgnjn_s) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b001)) + encoding(*format_r_fp_no_rm(0b1010011, 0b001, 0b0010000)) asm { 'fsgnjn.s {frd}, {frs1}, {frs2}' } code do - frd[] = fsgnjn32(frs1, frs2) + frd[] = f32_sign_injection_n(frs1, frs2) end end Instruction(:fsgnjn_d) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010001, funct3: 0b001)) + encoding(*format_r_fp_no_rm(0b1010011, 0b001, 0b0010001)) asm { 'fsgnjn.d {frd}, {frs1}, {frs2}' } code do - frd[] = fsgnjn64(frs1, frs2) + frd[] = f64_sign_injection_n(frs1, frs2) end end Instruction(:fsgnjx_s) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b010)) + encoding(*format_r_fp_no_rm(0b1010011, 0b010, 0b0010000)) asm { 'fsgnjx.s {frd}, {frs1}, {frs2}' } code do - frd[] = fsgnjx32(frs1, frs2) + frd[] = f32_sign_xor(frs1, frs2) end end Instruction(:fsgnjx_d) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010000, funct3: 0b010)) + encoding(*format_r_fp_no_rm(0b1010011, 0b010, 0b0010001)) asm { 'fsgnjx.d {frd}, {frs1}, {frs2}' } code do - frd[] = fsgnjx64(frs1, frs2) + frd[] = f64_sign_xor(frs1, frs2) end end # Comparisons Instruction(:fmin_s) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010100, funct3: 0b000)) + encoding(*format_r_fp_no_rm(0b1010011, 0b000, 0b0010100)) asm { 'fmin.s {frd}, {frs1}, {frs2}' } code do frd[] = f32_min(frs1, frs2) @@ -227,7 +251,7 @@ module RV64F end Instruction(:fmin_d) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010101, funct3: 0b000)) + encoding(*format_r_fp_no_rm(0b1010011, 0b000, 0b0010101)) asm { 'fmin.d {frd}, {frs1}, {frs2}' } code do frd[] = f64_min(frs1, frs2) @@ -235,7 +259,7 @@ module RV64F end Instruction(:fmax_s) do - encoding(*format_r_fp(0b1010011, funct7: 0b0010100, funct3: 0b001)) + encoding(*format_r_fp_no_rm(0b1010011, 0b001, 0b0010100)) asm { 'fmax.s {frd}, {frs1}, {frs2}' } code do frd[] = f32_max(frs1, frs2) @@ -243,15 +267,15 @@ module RV64F end Instruction(:fmax_d) do - encoding(*format_r_no_rm(0b1010011, funct7: 0b0010101, funct3: 0b001)) - asm { 'fmax.s {frd}, {frs1}, {frs2}' } + encoding(*format_r_fp_no_rm(0b1010011, 0b001, 0b0010101)) + asm { 'fmax.d {frd}, {frs1}, {frs2}' } code do frd[] = f64_max(frs1, frs2) end end Instruction(:feq_s) do - encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010000, funct3: 0b010)) + encoding(*format_r_fp_comp(0b1010011, 0b010, 0b1010000)) asm { 'feq.s {rd}, {frs1}, {frs2}' } code do rd[] = f32_eq(frs1, frs2) @@ -259,15 +283,15 @@ module RV64F end Instruction(:feq_d) do - encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010001, funct3: 0b010)) - asm { 'feq.s {rd}, {frs1}, {frs2}' } + encoding(*format_r_fp_comp(0b1010011, 0b010, 0b1010001)) + asm { 'feq.d {rd}, {frs1}, {frs2}' } code do rd[] = f64_eq(frs1, frs2) end end Instruction(:flt_s) do - encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010000, funct3: 0b001)) + encoding(*format_r_fp_comp(0b1010011, 0b001, 0b1010000)) asm { 'flt.s {rd}, {frs1}, {frs2}' } code do rd[] = f32_lt(frs1, frs2) @@ -275,7 +299,7 @@ module RV64F end Instruction(:flt_d) do - encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010001, funct3: 0b001)) + encoding(*format_r_fp_comp(0b1010011, 0b001, 0b1010001)) asm { 'flt.d {rd}, {frs1}, {frs2}' } code do rd[] = f64_lt(frs1, frs2) @@ -283,7 +307,7 @@ module RV64F end Instruction(:fle_s) do - encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010000, funct3: 0b000)) + encoding(*format_r_fp_comp(0b1010011, 0b000, 0b1010000)) asm { 'fle.s {rd}, {frs1}, {frs2}' } code do rd[] = f32_le(frs1, frs2) @@ -291,7 +315,7 @@ module RV64F end Instruction(:fle_d) do - encoding(*format_r_fp_comp(0b1010011, funct7: 0b1010001, funct3: 0b000)) + encoding(*format_r_fp_comp(0b1010011, 0b000, 0b1010001)) asm { 'fle.d {rd}, {frs1}, {frs2}' } code do rd[] = f64_le(frs1, frs2) @@ -300,7 +324,7 @@ module RV64F # Conversions Instruction(:fcvt_w_s) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00000)) + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00000, 0b1100000)) asm { 'fcvt.w.s {rd}, {frs1}' } code do rd[] = f32_to_i32(frs1) @@ -308,7 +332,7 @@ module RV64F end Instruction(:fcvt_wu_s) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00001)) + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00001, 0b1100000)) asm { 'fcvt.wu.s {rd}, {frs1}' } code do rd[] = f32_to_u32(frs1) @@ -316,7 +340,7 @@ module RV64F end Instruction(:fcvt_l_s) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00010)) + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00010, 0b1100000)) asm { 'fcvt.l.s {rd}, {frs1}' } code do rd[] = f32_to_i64(frs1) @@ -324,7 +348,7 @@ module RV64F end Instruction(:fcvt_lu_s) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1100000, funct5: 0b00011)) + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00011, 0b1100000)) asm { 'fcvt.lu.s {rd}, {frs1}' } code do rd[] = f32_to_u64(frs1) @@ -332,7 +356,7 @@ module RV64F end Instruction(:fcvt_s_w) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00000)) + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00000, 0b1101000)) asm { 'fcvt.s.w {frd}, {rs1}' } code do frd[] = i32_to_f32(rs1) @@ -340,7 +364,7 @@ module RV64F end Instruction(:fcvt_s_wu) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00001)) + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00001, 0b1101000)) asm { 'fcvt.s.wu {frd}, {rs1}' } code do frd[] = u32_to_f32(rs1) @@ -348,7 +372,7 @@ module RV64F end Instruction(:fcvt_s_l) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00010)) + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00010, 0b1101000)) asm { 'fcvt.s.l {frd}, {rs1}' } code do frd[] = i64_to_f32(rs1) @@ -356,7 +380,7 @@ module RV64F end Instruction(:fcvt_s_lu) do - encoding(*format_r_fp_fcvt(0b1010011, funct7: 0b1101000, funct5: 0b00011)) + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00011, 0b1101000)) asm { 'fcvt.s.lu {frd}, {rs1}' } code do frd[] = u64_to_f32(rs1) @@ -365,16 +389,24 @@ module RV64F # Classification Instruction(:fclass_s) do # might need separate format - encoding(*format_r_fp_comp(0b1010011, funct7: 0b1110000, funct3: 0b001)) + encoding(*format_r_fp_comp(0b1010011, 0b001, 0b1110000)) asm { 'fclass.s {rd}, {frs1}' } code do rd[] = f32_classify(frs1) end end + Instruction(:fclass_d) do # might need separate format + encoding(*format_r_fp_comp(0b1010011, 0b001, 0b1110000)) + asm { 'fclass.d {rd}, {frs1}' } + code do + rd[] = f64_classify(frs1) + end + end + # Move Instruction(:fmv_x_w) do - encoding(*format_r_fp(0b1010011, funct7: 0b1110000)) + encoding(*format_r_fp_no_rm_move_rd(0b1010011, 0b000, 0b1110000)) asm { 'fmv.x.w {rd}, {frs1}' } code do rd[] = frs1[31, 0] @@ -382,7 +414,7 @@ module RV64F end Instruction(:fmv_w_x) do - encoding(*format_r_fp(0b1010011, funct7: 0b1111000)) + encoding(*format_r_fp_no_rm_move_frd(0b1010011, 0b000, 0b1111000)) asm { 'fmv.w.x {frd}, {rs1}' } code do frd[] = rs1[31, 0] diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index 5dcd61a..04fb923 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -101,15 +101,48 @@ def format_r_fp_no_rm(opcode, funct3, funct7) ], freg(:frs2), freg(:frs1), freg(:frd)] end - def format_r_fp_fcvt(opcode, funct5, funct7) + def format_r_fp_fcvt_frd(opcode, funct5, funct7) [:R_FCVT, [ field(:f_opcode, 6, 0, opcode), field(:f_frd, 11, 7), field(:f_rm, 14, 12), + field(:f_rs1, 19, 15), + field(:f_funct5, 24, 20, funct5), + field(:f_funct7, 31, 25, funct7) + ], xreg(:rs1), freg(:frd)] + end + + def format_r_fp_fcvt_rd(opcode, funct5, funct7) + [:R_FCVT, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_rm, 14, 12), field(:f_frs1, 19, 15), field(:f_funct5, 24, 20, funct5), field(:f_funct7, 31, 25, funct7) - ], freg(:frs1), freg(:frd)] + ], freg(:frs1), xreg(:rd)] + end + + def format_r_fp_no_rm_move_rd(opcode, funct3, funct7) + [:R_FCVT, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_frs1, 19, 15), + field(:f_funct5, 24, 20, 0b00000), + field(:f_funct7, 31, 25, funct7) + ], freg(:frs1), xreg(:rd)] + end + + def format_r_fp_no_rm_move_frd(opcode, funct3, funct7) + [:R_FCVT, [ + field(:f_opcode, 6, 0, opcode), + field(:f_frd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_rs1, 19, 15), + field(:f_funct5, 24, 20, 0b00000), + field(:f_funct7, 31, 25, funct7) + ], xreg(:rs1), freg(:frd)] end def format_i(opcode, funct3) @@ -122,6 +155,16 @@ def format_i(opcode, funct3) ], i_imm(:imm), xreg(:rs1), xreg(:rd)] end + def format_i_fp(opcode, funct3) + [:I, [ + field(:f_opcode, 6, 0, opcode), + field(:f_frd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_rs1, 19, 15), + field(:f_imm, 31, 20) + ], i_imm(:imm), xreg(:rs1), freg(:frd)] + end + def format_i_shift(opcode, func3, sopcode) [:srai, [ field(:f_opcode, 6, 0, opcode), @@ -168,4 +211,15 @@ def format_s(opcode, func3) field(:f_imm11_5, 31, 25) ], s_imm(:imm), xreg(:rs1), xreg(:rs2)] end + + def format_s_fp(opcode, func3) + [:S, [ + field(:f_opcode, 6, 0, opcode), + field(:func3, 14, 12, func3), + field(:f_imm4_0, 11, 7), + field(:f_rs1, 19, 15), + field(:f_frs2, 24, 20), + field(:f_imm11_5, 31, 25) + ], s_imm(:imm), xreg(:rs1), freg(:frs2)] + end end diff --git a/lib/Utility/type.rb b/lib/Utility/type.rb index c5ae344..27f8d29 100644 --- a/lib/Utility/type.rb +++ b/lib/Utility/type.rb @@ -1,43 +1,47 @@ module Utility - # The instances of the Type class are immutable. - # Only one instance of a particular type is ever created - # and this instance is linked to one symbol. If two types - # are equal is a matter of doing a trivial symbol comparison. - class Type - attr_reader :name, :bitsize, :typeof - def initialize(name, bitsize, typeof) - @name = name; @bitsize = bitsize; @typeof = typeof; @sym = name.to_sym - end + # The instances of the Type class are immutable. + # Only one instance of a particular type is ever created + # and this instance is linked to one symbol. If two types + # are equal is a matter of doing a trivial symbol comparison. + class Type + attr_reader :name, :bitsize, :typeof + + def initialize(name, bitsize, typeof) + @name = name + @bitsize = bitsize + @typeof = typeof + @sym = name.to_sym end + end - $ExistingTypes = {} + $ExistingTypes = {} - # Returns the Type object associated with the symbol. - # Currently supported types: signed/unsigned integral, bit type, register type - # (like pointer in C++/C) - # If symbol is not [usbr][1-9][0-9]* the behaviour is undefined - # - # get_type :b33 -> # - # get_type :i32 -> # - # get_type :u64 -> # - def get_type(sym) - if $ExistingTypes.has_key?(sym) - return $ExistingTypes[sym] - end - sym_str = sym.to_s - $ExistingTypes[sym] = Type.new(sym_str, sym_str.scan(/\d+/).last.to_i, sym_str[0].to_sym) - return $ExistingTypes[sym] - end + # Returns the Type object associated with the symbol. + # Currently supported types: signed/unsigned integral, bit type, register type + # (like pointer in C++/C) + # If symbol is not [usbr][1-9][0-9]* the behaviour is undefined + # + # get_type :b33 -> # + # get_type :i32 -> # + # get_type :u64 -> # + def get_type(sym) + return $ExistingTypes[sym] if $ExistingTypes.has_key?(sym) + + sym_str = sym.to_s + puts "boop, #{sym_str}" + $ExistingTypes[sym] = Type.new(sym_str, sym_str.scan(/\d+/).last.to_i, sym_str[0].to_sym) + $ExistingTypes[sym] + end - # Checks if two symbols have the same underlying type. - # If symbol is not [usbr][1-9][0-9]* the behaviour is undefined - # - # equal_typeof :i32 :i36 -> true - # equal_typeof :u32 :i36 -> false - def equal_typeof(sym1, sym2) - return get_type(sym1).typeof == get_type(sym2).typeof - end + # Checks if two symbols have the same underlying type. + # If symbol is not [usbr][1-9][0-9]* the behaviour is undefined + # + # equal_typeof :i32 :i36 -> true + # equal_typeof :u32 :i36 -> false + def equal_typeof(sym1, sym2) + get_type(sym1).typeof == get_type(sym2).typeof + end -private_constant :Type -module_function :get_type, :equal_typeof + private_constant :Type + module_function :get_type, :equal_typeof end diff --git a/lib/ir_gen.rb b/lib/ir_gen.rb index f5efac4..7253594 100644 --- a/lib/ir_gen.rb +++ b/lib/ir_gen.rb @@ -3,9 +3,11 @@ require 'ADL/base' require 'ADL/builder' +puts 'boop' require 'Target/RISC-V/32I' +puts 'boop' require 'Target/RISC-V/64F' - +puts 'boop' require 'yaml' yaml_data = SimInfra.serialize diff --git a/sim_gen/ExecEngines/base_exec_engine.rb b/sim_gen/ExecEngines/base_exec_engine.rb index a2247aa..3f9fbde 100644 --- a/sim_gen/ExecEngines/base_exec_engine.rb +++ b/sim_gen/ExecEngines/base_exec_engine.rb @@ -1,4 +1,4 @@ -require "sim_gen/Utility/sim_utility" +require 'sim_gen/Utility/sim_utility' module SimGen module BaseExecEngine @@ -6,12 +6,16 @@ module Header module_function def generate_base_exec_engine(input_ir) -"#ifndef GENERATED_#{input_ir[:isa_name].upcase}_EXEC_ENGINE_HH_INCLUDED + "#ifndef GENERATED_#{input_ir[:isa_name].upcase}_EXEC_ENGINE_HH_INCLUDED #define GENERATED_#{input_ir[:isa_name].upcase}_EXEC_ENGINE_HH_INCLUDED #include \"cpu_state.hh\" #include \"isa.hh\" #include \"memory.hh\" +extern \"C\" { +#include \"softfloat.h\" +} + namespace prot::engine { using namespace prot::state; @@ -34,11 +38,14 @@ module TranslationUnit module_function def generate_base_exec_engine(input_ir) - max_xlen = SimGen::Helper::find_max_xlen(input_ir[:regfiles]) + max_xlen = SimGen::Helper.find_max_xlen(input_ir[:regfiles]) -"#include \"base_exec_engine.hh\" + "#include \"base_exec_engine.hh\" #include \"memory.hh\" #include \"decoder.hh\" +extern \"C\" { +#include \"softfloat.h\" +} namespace prot::engine { using namespace prot::state; @@ -53,7 +60,8 @@ def generate_base_exec_engine(input_ir) cpu.increaseICount(); } } // namespace prot::engine -" end +" + end end end end From d8558a1a073ce6475c8db91da9ceb9eaf04ac0f1 Mon Sep 17 00:00:00 2001 From: doushe821 Date: Wed, 11 Mar 2026 23:38:20 +0300 Subject: [PATCH 4/8] [Emitter] Minor progress in fixing type issues Safety commit --- lib/ADL/scope.rb | 7 ++++++- lib/ADL/var.rb | 1 - lib/Target/RISC-V/64F.rb | 8 ++++---- lib/Target/RISC-V/encoding.rb | 11 +++++++++++ lib/Utility/helper_cpp.rb | 23 +++++++++++++++++++++-- lib/Utility/type.rb | 1 - lib/ir_gen.rb | 3 --- ser2ruby/base2ruby.rb | 10 ++++------ 8 files changed, 46 insertions(+), 18 deletions(-) diff --git a/lib/ADL/scope.rb b/lib/ADL/scope.rb index 8b5fa56..60dcb31 100644 --- a/lib/ADL/scope.rb +++ b/lib/ADL/scope.rb @@ -163,6 +163,7 @@ def u32_to_f32(a) = unOp(a, :u32_to_f32) def i64_to_f32(a) = unOp(a, :i64_to_f32) def u64_to_f32(a) = unOp(a, :u64_to_f32) def f32_classify(a) = unOp(a, :f32_classify) + def f64_classify(a) = unOp(a, :f64_classify) def select(p, a, b) a = resolve_const(a) @@ -241,7 +242,11 @@ def stmt(name, operands, attrs = nil) def read_transform(operation_name, op) if op.class == Var && !op.regset.nil? - x = tmpvar(('b' + op.type.to_s[1..-1]).to_sym) + case op.regset + when :XRegs then x = tmpvar(('b' + op.type.to_s[1..-1]).to_sym) + when :FRegs then x = tmpvar(('f' + op.type.to_s[1..-1]).to_sym) + else raise 'Unknown regset' + end @tree << IrStmt.new(:readReg, [x, op], nil) x else diff --git a/lib/ADL/var.rb b/lib/ADL/var.rb index 9dec20d..58126f0 100644 --- a/lib/ADL/var.rb +++ b/lib/ADL/var.rb @@ -63,7 +63,6 @@ def r(regset) = @scope.get_reg(self, regset, ('r' + Utility.get_type(@type).bits def method_missing(name, *regset) if regset.empty? - puts "name = #{name}" instance_eval "def #{name}(); @scope.cast(self, (#{name}).to_sym); end", __FILE__, __LINE__ @scope.cast(self, name.to_sym) else diff --git a/lib/Target/RISC-V/64F.rb b/lib/Target/RISC-V/64F.rb index 629fec3..7772aae 100644 --- a/lib/Target/RISC-V/64F.rb +++ b/lib/Target/RISC-V/64F.rb @@ -388,16 +388,16 @@ module RV64F end # Classification - Instruction(:fclass_s) do # might need separate format - encoding(*format_r_fp_comp(0b1010011, 0b001, 0b1110000)) + Instruction(:fclass_s) do + encoding(*format_r_fp_class(0b1010011, 0b001, 0b00000, 0b1110000)) asm { 'fclass.s {rd}, {frs1}' } code do rd[] = f32_classify(frs1) end end - Instruction(:fclass_d) do # might need separate format - encoding(*format_r_fp_comp(0b1010011, 0b001, 0b1110000)) + Instruction(:fclass_d) do + encoding(*format_r_fp_class(0b1010011, 0b001, 0b00000, 0b1110001)) asm { 'fclass.d {rd}, {frs1}' } code do rd[] = f64_classify(frs1) diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index 04fb923..f92d667 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -90,6 +90,17 @@ def format_r_fp_comp(opcode, funct3, funct7) ], freg(:frs2), freg(:frs1), xreg(:rd)] end + def format_r_fp_class(opcode, funct3, funct5, funct7) + [:R_FP_CLASS, [ + field(:f_opcode, 6, 0, opcode), + field(:f_rd, 11, 7), + field(:f_funct3, 14, 12, funct3), + field(:f_frs1, 19, 15), + field(:f_funct5, 24, 20, funct5), + field(:f_funct7, 31, 25, funct7) + ], freg(:frs1), xreg(:rd)] + end + def format_r_fp_no_rm(opcode, funct3, funct7) [:R_FP_NO_RM, [ field(:f_opcode, 6, 0, opcode), diff --git a/lib/Utility/helper_cpp.rb b/lib/Utility/helper_cpp.rb index d54f32f..39628b9 100644 --- a/lib/Utility/helper_cpp.rb +++ b/lib/Utility/helper_cpp.rb @@ -9,14 +9,33 @@ module HelperCpp def gen_type(type) actual_type = Utility.get_type(type) + cpp_bitsize = actual_type.bitsize % 32 == 0 ? actual_type.bitsize : (actual_type.bitsize / 32 + 1) * 32 - "#{actual_type.typeof == :s ? 'int' : 'uint'}#{cpp_bitsize}_t" + # NOTE: while initializing m_pc, its :size is given as an argument + # get_type is actually fine with that and considers it an int, however: + # 1. It should be 64 bit (at least after adding any of RV64 extension) + # 2. I feel like this can cause some issues later. + # So that's why instead of tracking errors inside the case, + # we assume every 'unknown' type to be unsigned, even if it's some random + # value like '3'(integer) in case of pc initialization in cpu_state.rb + # I am not sure if this is intentional or just haven't been fixed yet, + # so I will only leave this comment for now. + case actual_type.typeof + when :s then "int#{cpp_bitsize}_t" + when :f then "float#{cpp_bitsize}_t" + else "uint#{cpp_bitsize}_t" + end end def gen_small_type(type) actual_type = Utility.get_type(type) cpp_bitsize = actual_type.bitsize % 8 == 0 ? actual_type.bitsize : (actual_type.bitsize / 8 + 1) * 8 - "#{actual_type.typeof == :s ? 'int' : 'uint'}#{cpp_bitsize}_t" + + case actual_type.typeof + when :s then "int#{cpp_bitsize}_t" + when :f then "float#{cpp_bitsize}_t" + else "uint#{cpp_bitsize}_t" + end end end end diff --git a/lib/Utility/type.rb b/lib/Utility/type.rb index 27f8d29..ebe4b38 100644 --- a/lib/Utility/type.rb +++ b/lib/Utility/type.rb @@ -28,7 +28,6 @@ def get_type(sym) return $ExistingTypes[sym] if $ExistingTypes.has_key?(sym) sym_str = sym.to_s - puts "boop, #{sym_str}" $ExistingTypes[sym] = Type.new(sym_str, sym_str.scan(/\d+/).last.to_i, sym_str[0].to_sym) $ExistingTypes[sym] end diff --git a/lib/ir_gen.rb b/lib/ir_gen.rb index 7253594..cb99d3d 100644 --- a/lib/ir_gen.rb +++ b/lib/ir_gen.rb @@ -3,11 +3,8 @@ require 'ADL/base' require 'ADL/builder' -puts 'boop' require 'Target/RISC-V/32I' -puts 'boop' require 'Target/RISC-V/64F' -puts 'boop' require 'yaml' yaml_data = SimInfra.serialize diff --git a/ser2ruby/base2ruby.rb b/ser2ruby/base2ruby.rb index b119979..7ef3171 100644 --- a/ser2ruby/base2ruby.rb +++ b/ser2ruby/base2ruby.rb @@ -1,4 +1,4 @@ -require "Utility/gen_emitter" +require 'Utility/gen_emitter' # Helper methods for Intermediate Representation module IRHelper @@ -55,12 +55,11 @@ def generate_instructions(instructions) def ir2ruby(ir) isa_name = ir[:isa_name] - + regfiles = generate_regfiles(ir[:regfiles]).to_s instructions = generate_instructions(ir[:instructions]).to_s - -"module #{isa_name.upcase} + "module #{isa_name.upcase} #{regfiles} #{instructions} @@ -69,10 +68,9 @@ def ir2ruby(ir) end end - require 'yaml' yaml_data = YAML.load_file('sim_lib/generated/IR.yaml') -yaml_data[:isa_name] = "RISCV" +yaml_data[:isa_name] = 'RISCV' puts IRHelper.ir2ruby(yaml_data) From 95b2957ea89bf414fa71610d9e0bf81ede5e9c15 Mon Sep 17 00:00:00 2001 From: doushe821 Date: Sat, 14 Mar 2026 22:46:26 +0300 Subject: [PATCH 5/8] [Emitter] Described typing for almost all instructions. TODO: 1. Do neg correctly. 2. Add rounding mode everywhere, for that I also need to change cpu_state emitter a bit. 3. Move most fp-relateed functions in separate module of emitter. 4. Tests. --- code_gen/cpp_gen.rb | 326 ++++++++++++++++++++++++++-------- lib/Target/RISC-V/encoding.rb | 2 +- lib/Utility/fp_helper_cpp.rb | 25 +++ lib/Utility/helper_cpp.rb | 23 +-- sim_gen/CPUState/cpu_state.rb | 8 +- 5 files changed, 284 insertions(+), 100 deletions(-) create mode 100644 lib/Utility/fp_helper_cpp.rb diff --git a/code_gen/cpp_gen.rb b/code_gen/cpp_gen.rb index df68fca..af4617f 100644 --- a/code_gen/cpp_gen.rb +++ b/code_gen/cpp_gen.rb @@ -1,4 +1,5 @@ require 'Utility/helper_cpp' +require 'Utility/fp_helper_cpp' # frozen_string_literal: true # Semantics Generator: Converts IR to C++ code @@ -20,30 +21,148 @@ def binary_operation(emitter, operation, op_str) src2 = src2.nil? ? operation[:oprnds][2][:value] : src2 emitter.emit_line("#{dst} = #{src1} #{op_str} #{src2};") - end - def emit_fp_binary(opname, operation) + def emit_fp_binary(opname, operation, dst_type:, src_types:) dst = map_operand(operation[:oprnds][0]) src1 = map_operand(operation[:oprnds][1]) src2 = map_operand(operation[:oprnds][2]) - @emitter.emit_line("#{dst} = #{opname}(#{src1}, #{src2});") + t1 = Utility.gen_typed_tmp(src1, src_types[0]) + t2 = Utility.gen_typed_tmp(src2, src_types[1]) + tr = Utility.gen_typed_tmp(dst, dst_type) + + if Utility::FP_INFO[src_types[0]] + info = Utility::FP_INFO[src_types[0]] + @emitter.emit_line("#{info[:c_type]} #{t1} = { #{info[:unpack]}(#{src1}) };") + else + ctype = Utility::INT_INFO[src_types[0]] + @emitter.emit_line("#{ctype} #{t1} = (#{ctype})(#{src1});") + end + + if Utility::FP_INFO[src_types[1]] + info = Utility::FP_INFO[src_types[1]] + @emitter.emit_line("#{info[:c_type]} #{t2} = { #{info[:unpack]}(#{src2}) };") + else + ctype = Utility::INT_INFO[src_types[1]] + @emitter.emit_line("#{ctype} #{t2} = (#{ctype})(#{src2});") + end + + if Utility::FP_INFO[dst_type] + ctype = Utility::FP_INFO[dst_type][:c_type] + @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{t1}, #{t2});") + pack = Utility::FP_INFO[dst_type][:pack] + @emitter.emit_line("#{dst} = #{pack % tr};") + else + ctype = Utility::INT_INFO[dst_type] + @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{t1}, #{t2});") + @emitter.emit_line("#{dst} = (uint64_t)#{tr};") + end + end + + def gen_fp_neg(tmp_name, type) + case type + when :f32 then "#{tmp_name}.v ^= 0x80000000U;" + when :f64 then "#{tmp_name}.v ^= 0x8000000000000000ULL;" + end end - def emit_fp_unary(opname, operation) + def emit_fp_unary(opname, operation, dst_type:, src_type:) dst = map_operand(operation[:oprnds][0]) src = map_operand(operation[:oprnds][1]) - @emitter.emit_line("#{dst} = #{opname}(#{src});") + ts = Utility.gen_typed_tmp(src, src_type) + tr = Utility.gen_typed_tmp(dst, dst_type) + + if Utility::FP_INFO[src_type] + info = Utility::FP_INFO[src_type] + @emitter.emit_line("#{info[:c_type]} #{ts} = { #{info[:unpack]}(#{src}) };") + else + ctype = Utility::INT_INFO[src_type] + @emitter.emit_line("#{ctype} #{ts} = (#{ctype})(#{src});") + end + + if Utility::FP_INFO[dst_type] + ctype = Utility::FP_INFO[dst_type][:c_type] + @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{ts});") + pack = Utility::FP_INFO[dst_type][:pack] + @emitter.emit_line("#{dst} = #{pack % tr};") + else + ctype = Utility::INT_INFO[dst_type] + @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{ts});") + @emitter.emit_line("#{dst} = (uint64_t)#{tr};") + end end - def emit_fp_ternary(opname, operation) - dst = map_operand(operation[:oprnds][0]) + def emit_fp_ternary(opname, operation, + dst_type:, + src_types:, + negate_src: []) + dst = map_operand(operation[:oprnds][0]) src1 = map_operand(operation[:oprnds][1]) src2 = map_operand(operation[:oprnds][2]) src3 = map_operand(operation[:oprnds][3]) - @emitter.emit_line("#{dst} = #{opname}(#{src1}, #{src2}, #{src3});") + srcs = [src1, src2, src3] + svars = [] + + srcs.each_with_index do |src, i| + ty = src_types[i] + t = Utility.gen_typed_tmp(src, ty) + svars << t + + if Utility::FP_INFO[ty] + info = Utility::FP_INFO[ty] + @emitter.emit_line("#{info[:c_type]} #{t} = { #{info[:unpack]}(#{src}) };") + if negate_src.include?(i) + negf = ty == :f32 ? 'f32_neg' : 'f64_neg' + @emitter.emit_line("#{t} = #{negf}(#{t});") + end + + else + ctype = Utility::INT_INFO[ty] + @emitter.emit_line("#{ctype} #{t} = (#{ctype})(#{src});") + + @emitter.emit_line("#{t} = -#{t};") if negate_src.include?(i) + end + end + + tr = Utility.gen_typed_tmp(dst, dst_type) + if Utility::FP_INFO[dst_type] + ctype = Utility::FP_INFO[dst_type][:c_type] + @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{svars.join(', ')});") + pack = Utility::FP_INFO[dst_type][:pack] + @emitter.emit_line("#{dst} = #{pack % tr};") + else + ctype = Utility::INT_INFO[dst_type] + @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{svars.join(', ')});") + @emitter.emit_line("#{dst} = (uint64_t)#{tr};") + end + end + + def emit_sign_inject(operation, width:, mode:) + dst, src1, src2 = map_n_operands(operation, 3) + + mag_mask, sign_mask = + if width == 32 + [Utility::F32_MAG_MASK, Utility::F32_SIGN_MASK] + else + [Utility::F64_MAG_MASK, Utility::F64_SIGN_MASK] + end + + sign_expr = + case mode + when :copy then "(#{src2} & #{sign_mask})" + when :neg then "(~#{src2} & #{sign_mask})" + when :xor then "(#{src1} ^ (#{src2} & #{sign_mask}))" + end + + result = if mode == :xor + sign_expr + else + "(#{src1} & #{mag_mask}) | #{sign_expr}" + end + result = "0xFFFFFFFF00000000ULL | (uint64_t)(#{result})" if width == 32 + @emitter.emit_line("#{dst} = #{result};") end def map_n_operands(op, n) @@ -139,10 +258,10 @@ def generate_statement(operation) src_name = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name] expr = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name] expr = expr.nil? ? operation[:oprnds][0][:value] : expr - @emitter.emit_line("#{expr} = #{cpu_read_reg(src)}<#{Utility::HelperCpp.gen_small_type(src[:type])}>(#{src_name});") when :writeReg dst = operation[:oprnds][0] + src = operation[:oprnds][1] dst_name = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name] expr = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name] expr = expr.nil? ? operation[:oprnds][1][:value] : expr @@ -174,75 +293,134 @@ def generate_statement(operation) @emitter.emit_line("#{dst} = #{cond} ? #{true_val} : #{false_val};") - # Floating point arithmetic binary operations - when :f32_add then emit_fp_binary('f32_add', operation) - when :f64_add then emit_fp_binary('f64_add', operation) - - when :f32_sub then emit_fp_binary('f32_sub', operation) - when :f64_sub then emit_fp_binary('f64_sub', operation) - - when :f32_mul then emit_fp_binary('f32_mul', operation) - when :f64_mul then emit_fp_binary('f64_mul', operation) - - when :f32_div then emit_fp_binary('f32_div', operation) - when :f64_div then emit_fp_binary('f64_div', operation) - # Floating point unary operations - when :f32_sqrt then emit_fp_unary('f32_sqrt', operation) - when :f64_sqrt then emit_fp_unary('f64_sqrt', operation) - # Floating point fused operations - when :f32_mul_add then emit_fp_ternary('f32_mulAdd', operation) - when :f64_mul_add then emit_fp_ternary('f64_mulAdd', operation) - when :f32_mul_sub - dst, src1, src2, src3 = map_n_operands(operation, 4) - @emitter.emit_line("#{dst} = f32_mulAdd(#{src1}, #{src2}, -#{src3});") + # Floating point arithmetic operations + when :f32_add then emit_fp_binary('f32_add', operation, + dst_type: :f32, + src_types: %i[f32 f32]) + + when :f64_add then emit_fp_binary('f64_add', operation, + dst_type: :f64, + src_types: %i[f64 f64]) + + when :f32_sub then emit_fp_binary('f32_sub', operation, + dst_type: :f32, + src_types: %i[f32 f32]) + + when :f64_sub then emit_fp_binary('f64_sub', operation, + dst_type: :f64, + src_types: %i[f64 f64]) + + when :f32_mul then emit_fp_binary('f32_mul', operation, + dst_type: :f32, + src_types: %i[f32 f32]) + + when :f64_mul then emit_fp_binary('f64_mul', operation, + dst_type: :f64, + src_types: %i[f64 f64]) + + when :f32_div then emit_fp_binary('f32_div', operation, + dst_type: :f32, + src_types: %i[f32 f32]) + + when :f64_div then emit_fp_binary('f64_div', operation, + dst_type: :f64, + src_types: %i[f64 f64]) + when :f32_sqrt then emit_fp_unary('f32_sqrt', operation, + dst_type: :f32, + src_type: :f32) + + when :f64_sqrt then emit_fp_unary('f64_sqrt', operation, + dst_type: :f64, + src_type: :f64) + when :f32_mul_add then emit_fp_ternary('f32_mulAdd', operation, + dst_type: :f32, + src_types: %i[f32 f32 f32]) + + when :f64_mul_add then emit_fp_ternary('f64_mulAdd', operation, + dst_type: :f64, + src_types: %i[f64 f64 f64]) when :f64_mul_sub - dst, src1, src2, src3 = map_n_operands(operation, 4) - @emitter.emit_line("#{dst} = f64_mulAdd(#{src1}, #{src2}, -#{src3});") - when :f32_mul_sub_n - dst, src1, src2, src3 = map_n_operands(operation, 4) - @emitter.emit_line("#{dst} = f32_mulAdd(-#{src1}, #{src2}, -#{src3});") + emit_fp_ternary('f64_mulAdd', operation, + dst_type: :f64, + src_types: %i[f64 f64 f64], + negate_src: [3]) when :f64_mul_sub_n - dst, src1, src2, src3 = map_n_operands(operation, 4) - @emitter.emit_line("#{dst} = f64_mulAdd(-#{src1}, #{src2}, -#{src3});") - # Floating point comparison operations - when :f32_eq then emit_fp_binary('f32_eq', operation) - when :f64_eq then emit_fp_binary('f64_eq', operation) - when :f32_lt then emit_fp_binary('f32_lt', operation) - when :f64_lt then emit_fp_binary('f64_lt', operation) - when :f32_le then emit_fp_binary('f32_le', operation) - when :f64_le then emit_fp_binary('f64_le', operation) - when :f32_min then emit_fp_binary('f32_min', operation) - when :f64_min then emit_fp_binary('f64_min', operation) - when :f32_max then emit_fp_binary('f32_max', operation) - when :f64_max then emit_fp_binary('f64_max', operation) + emit_fp_ternary('f64_mulAdd', operation, + dst_type: :f64, + src_types: %i[f64 f64 f64], + negate_src: [1, 3]) + when :f32_eq then emit_fp_binary('f32_eq', operation, + dst_type: :i32, + src_types: %i[f32 f32]) + when :f64_eq then emit_fp_binary('f64_eq', operation, + dst_type: :i32, + src_types: %i[f64 f64]) + when :f32_lt then emit_fp_binary('f32_lt', operation, + dst_type: :i32, + src_types: %i[f32 f32]) + when :f64_lt then emit_fp_binary('f64_lt', operation, + dst_type: :i32, + src_types: %i[f64 f64]) + when :f32_le then emit_fp_binary('f32_le', operation, + dst_type: :i32, + src_types: %i[f32 f32]) + when :f64_le then emit_fp_binary('f64_le', operation, + dst_type: :i32, + src_types: %i[f64 f64]) + when :f32_min then emit_fp_binary('f32_min', operation, + dst_type: :f32, + src_types: %i[f32 f32]) + when :f64_min then emit_fp_binary('f64_min', operation, + dst_type: :f64, + src_types: %i[f64 f64]) + when :f32_max then emit_fp_binary('f32_max', operation, + dst_type: :f32, + src_types: %i[f32 f32]) + when :f64_max then emit_fp_binary('f64_max', operation, + dst_type: :f64, + src_types: %i[f64 f64]) + when :f32_mul_sub then emit_fp_ternary('f32_mulAdd', operation, + dst_type: :f32, + src_types: %i[f32 f32 f32], + negate_src: [2]) + when :f32_mul_sub_n then emit_fp_ternary('f32_mulAdd', operation, + dst_type: :f32, + src_types: %i[f32 f32 f32], + negate_src: [0, 2]) + # Floating point injections - when :f32_sign_injection - dst, src1, src2 = map_n_operands(operation, 3) - @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffff) | (#{src2} & 0x80000000);") - when :f64_sign_injection - dst, src1, src2 = map_n_operands(operation, 3) - @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffffffffffffULL) | (#{src2} & 0x8000000000000000ULL);") - when :f32_sign_injection_n - dst, src1, src2 = map_n_operands(operation, 3) - @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffff) | (~#{src2} & 0x80000000);") - when :f64_sign_injection_n - dst, src1, src2 = map_n_operands(operation, 3) - @emitter.emit_line("#{dst} = (#{src1} & 0x7fffffffffffffffULL) | (~#{src2} & 0x8000000000000000ULL);") - when :f32_sign_xor - dst, src1, src2 = map_n_operands(operation, 3) - @emitter.emit_line("#{dst} = #{src1} ^ (#{src2} & 0x80000000);") - when :f64_sign_xor - dst, src1, src2 = map_n_operands(operation, 3) - @emitter.emit_line("#{dst} = #{src1} ^ (#{src2} & 0x8000000000000000ULL);") + when :f32_sign_injection then emit_sign_inject(operation, width: 32, mode: :copy) + when :f64_sign_injection then emit_sign_inject(operation, width: 64, mode: :copy) + + when :f32_sign_injection_n then emit_sign_inject(operation, width: 32, mode: :neg) + when :f64_sign_injection_n then emit_sign_inject(operation, width: 64, mode: :neg) + + when :f32_sign_xor then emit_sign_inject(operation, width: 32, mode: :xor) + when :f64_sign_xor then emit_sign_inject(operation, width: 64, mode: :xor) # Floating point conversions - when :f32_to_i32 then emit_fp_unary('f32_to_i32', operation) - when :f32_to_u32 then emit_fp_unary('f32_to_ui32', operation) - when :f32_to_i64 then emit_fp_unary('f32_to_i64', operation) - when :f32_to_u64 then emit_fp_unary('f32_to_ui64', operation) - when :i32_to_f32 then emit_fp_unary('i32_to_f32', operation) - when :u32_to_f32 then emit_fp_unary('ui32_to_f32', operation) - when :i64_to_f32 then emit_fp_unary('i64_to_f32', operation) - when :u64_to_f32 then emit_fp_unary('ui64_to_f32', operation) + when :f32_to_i32 then emit_fp_unary('f32_to_i32', operation, + dst_type: :i32, src_type: :f32) + + when :f32_to_u32 then emit_fp_unary('f32_to_ui32', operation, + dst_type: :u32, src_type: :f32) + + when :f32_to_i64 then emit_fp_unary('f32_to_i64', operation, + dst_type: :i64, src_type: :f32) + + when :f32_to_u64 then emit_fp_unary('f32_to_ui64', operation, + dst_type: :u64, src_type: :f32) + + when :i32_to_f32 then emit_fp_unary('i32_to_f32', operation, + dst_type: :f32, src_type: :i32) + + when :u32_to_f32 then emit_fp_unary('ui32_to_f32', operation, + dst_type: :f32, src_type: :u32) + + when :i64_to_f32 then emit_fp_unary('i64_to_f32', operation, + dst_type: :f32, src_type: :i64) + + when :u64_to_f32 then emit_fp_unary('ui64_to_f32', operation, + dst_type: :f32, src_type: :u64) # Classification when :f32_classify dst, src = map_n_operands(operation, 2) diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index f92d667..f9efd01 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -31,7 +31,7 @@ def xreg(name) [name, :r32, "let :#{name}, :XRegs, [:op], :r32, f_#{name}"] end - def freg(name) + def freg64(name) [name, :f64, "let :#{name}, :FRegs, [:op], :f64, f_#{name}"] end end diff --git a/lib/Utility/fp_helper_cpp.rb b/lib/Utility/fp_helper_cpp.rb new file mode 100644 index 0000000..d1400c0 --- /dev/null +++ b/lib/Utility/fp_helper_cpp.rb @@ -0,0 +1,25 @@ +# Utility methods for FP instructions generation +module Utility + extend Utility + FP_INFO = { + f32: { c_type: 'float32_t', unpack: '(uint32_t)', pack: '0xFFFFFFFF00000000ULL | (uint64_t)%s.v' }, + f64: { c_type: 'float64_t', unpack: '(uint64_t)', pack: '(uint64_t)%s.v' } + }.freeze + + INT_INFO = { + i32: 'int32_t', + u32: 'uint32_t', + i64: 'int64_t', + u64: 'uint64_t' + }.freeze + + def gen_typed_tmp(base, type) + "#{base}_#{type}" + end + + F32_MAG_MASK = '0x7fffffffU'.freeze + F32_SIGN_MASK = '0x80000000U'.freeze + + F64_MAG_MASK = '0x7fffffffffffffffULL'.freeze + F64_SIGN_MASK = '0x8000000000000000ULL'.freeze +end diff --git a/lib/Utility/helper_cpp.rb b/lib/Utility/helper_cpp.rb index 39628b9..d54f32f 100644 --- a/lib/Utility/helper_cpp.rb +++ b/lib/Utility/helper_cpp.rb @@ -9,33 +9,14 @@ module HelperCpp def gen_type(type) actual_type = Utility.get_type(type) - cpp_bitsize = actual_type.bitsize % 32 == 0 ? actual_type.bitsize : (actual_type.bitsize / 32 + 1) * 32 - # NOTE: while initializing m_pc, its :size is given as an argument - # get_type is actually fine with that and considers it an int, however: - # 1. It should be 64 bit (at least after adding any of RV64 extension) - # 2. I feel like this can cause some issues later. - # So that's why instead of tracking errors inside the case, - # we assume every 'unknown' type to be unsigned, even if it's some random - # value like '3'(integer) in case of pc initialization in cpu_state.rb - # I am not sure if this is intentional or just haven't been fixed yet, - # so I will only leave this comment for now. - case actual_type.typeof - when :s then "int#{cpp_bitsize}_t" - when :f then "float#{cpp_bitsize}_t" - else "uint#{cpp_bitsize}_t" - end + "#{actual_type.typeof == :s ? 'int' : 'uint'}#{cpp_bitsize}_t" end def gen_small_type(type) actual_type = Utility.get_type(type) cpp_bitsize = actual_type.bitsize % 8 == 0 ? actual_type.bitsize : (actual_type.bitsize / 8 + 1) * 8 - - case actual_type.typeof - when :s then "int#{cpp_bitsize}_t" - when :f then "float#{cpp_bitsize}_t" - else "uint#{cpp_bitsize}_t" - end + "#{actual_type.typeof == :s ? 'int' : 'uint'}#{cpp_bitsize}_t" end end end diff --git a/sim_gen/CPUState/cpu_state.rb b/sim_gen/CPUState/cpu_state.rb index d75201f..8b29cd7 100644 --- a/sim_gen/CPUState/cpu_state.rb +++ b/sim_gen/CPUState/cpu_state.rb @@ -132,10 +132,10 @@ def generate_do_exit_func def generate_dump_func(regfiles) emitter = Utility::GenEmitter.new - emitter.emit_line("void CPU::dump(std::ostream &ost) const {") + emitter.emit_line('void CPU::dump(std::ostream &ost) const {') emitter.increase_indent - emitter.emit_line("fmt::println(ost, \"---CPU STATE DUMP---\");") + emitter.emit_line('fmt::println(ost, "---CPU STATE DUMP---");') regfiles.each do |regfile| regfile[:regs].each do |register| emitter.emit_line("fmt::print(ost, \"X[{:02}] = {:#010x} \", effIdx, get#{regfile[:name]}());") @@ -154,7 +154,7 @@ module SimGen module CPUState module Header module_function - + def generate_cpu_state(input_ir) pc_decl = Helper.generate_pc_decl(input_ir[:regfiles]) pc_functions = Helper.generate_pc_functions(input_ir[:regfiles]) @@ -165,7 +165,7 @@ def generate_cpu_state(input_ir) increase_icount_func = Helper.increase_icount_func base_type = Utility::HelperCpp.gen_type input_ir[:regfiles][0][:regs][0][:size] -"#ifndef GENERATED_#{input_ir[:isa_name].upcase}_CPUSTATE_HH_INCLUDED + "#ifndef GENERATED_#{input_ir[:isa_name].upcase}_CPUSTATE_HH_INCLUDED #define GENERATED_#{input_ir[:isa_name].upcase}_CPUSTATE_HH_INCLUDED #include \"memory.hh\" From e3de017503404c20bda47759f5a25931484aec18 Mon Sep 17 00:00:00 2001 From: doushe821 Date: Wed, 15 Apr 2026 21:42:19 +0300 Subject: [PATCH 6/8] [DSL] Added factorization over rounding mode, fixed min/max/neg functions, added NaN checks that do not use isNaN from softfloat. --- cmake/softfloat.cmake | 47 ++++-- code_gen/cpp_gen.rb | 286 ++++++++++++++++++++++++++-------- lib/ADL/scope.rb | 129 ++++++++------- lib/Target/RISC-V/64F.rb | 181 +++++++++++---------- lib/Target/RISC-V/encoding.rb | 18 +-- sim_gen/Decoders/decoder.rb | 2 + 6 files changed, 432 insertions(+), 231 deletions(-) diff --git a/cmake/softfloat.cmake b/cmake/softfloat.cmake index 2debb02..cf4939f 100644 --- a/cmake/softfloat.cmake +++ b/cmake/softfloat.cmake @@ -1,4 +1,3 @@ - # softfloat: floating point arithmetic library CPMAddPackage( NAME softfloat @@ -8,13 +7,8 @@ CPMAddPackage( EXCLUDE_FROM_ALL True SYSTEM True) -# IMPORTANT: since official GitHub doesn't use cmake, integrating purely with -# CPM is impossible. - -# Has to be generaterd to avoid using softfloat's makefiles in order to keep -# everything in one build system +# Generate platform.h set(SOFTFLOAT_PLATFORM_H ${CMAKE_CURRENT_BINARY_DIR}/platform.h) - file(WRITE ${SOFTFLOAT_PLATFORM_H} "#ifndef SOFTFLOAT_PLATFORM_H #define SOFTFLOAT_PLATFORM_H @@ -25,22 +19,45 @@ file(WRITE ${SOFTFLOAT_PLATFORM_H} #endif ") +# Collect all required source files file(GLOB SOFTFLOAT_SRC ${softfloat_SOURCE_DIR}/source/*.c + ${softfloat_SOURCE_DIR}/source/s_*.c + ${softfloat_SOURCE_DIR}/source/specialize/*.c ) -list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*F80*") -list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*F128*") -list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*bf16*") -list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*f16_*") - +# Select platform-specific primitives based on system +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64") + file(GLOB SOFTFLOAT_PLATFORM_SRC + ${softfloat_SOURCE_DIR}/source/8086-SSE/*.c + ) + set(SOFTFLOAT_PLATFORM_DIR ${softfloat_SOURCE_DIR}/source/8086-SSE) +else() + file(GLOB SOFTFLOAT_PLATFORM_SRC + ${softfloat_SOURCE_DIR}/source/generic/*.c + ) + set(SOFTFLOAT_PLATFORM_DIR ${softfloat_SOURCE_DIR}/source/generic) +endif() + +list(APPEND SOFTFLOAT_SRC ${SOFTFLOAT_PLATFORM_SRC}) + +# Exclude unsupported formats +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*F80.*") +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*F128.*") +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*bf16.*") +list(FILTER SOFTFLOAT_SRC EXCLUDE REGEX ".*f16_.*") + +# Create the static library add_library(softfloat STATIC ${SOFTFLOAT_SRC}) target_include_directories(softfloat PUBLIC ${softfloat_SOURCE_DIR}/source/include - ${softfloat_SOURCE_DIR}/build/Linux-x86_64-GCC - ${softfloat_SOURCE_DIR}/source/8086 + ${softfloat_SOURCE_DIR}/source + ${SOFTFLOAT_PLATFORM_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) -target_compile_definitions(softfloat PRIVATE SOFTFLOAT_FAST_INT64) +target_compile_definitions(softfloat PRIVATE + SOFTFLOAT_FAST_INT64 + SOFTFLOAT_FAST_INT64 +) diff --git a/code_gen/cpp_gen.rb b/code_gen/cpp_gen.rb index af4617f..0f92cf5 100644 --- a/code_gen/cpp_gen.rb +++ b/code_gen/cpp_gen.rb @@ -7,6 +7,20 @@ module CodeGen class CppGenerator attr_reader :emitter, :mapping + # Maybe should move it to other module + # RISC-V RM to SoftFloat RM mapping + # RNE=0 -> softfloat_round_near_even(0), RTZ=1 -> softfloat_round_minMag(1), + # RDN=2 -> softfloat_round_min(2), RUP=3 -> softfloat_round_max(3), + # RMM=4 -> softfloat_round_near_maxMag(4), DYN=7 -> read from fcsr + SOFTFLOAT_RM_MAP = { + 0 => 'softfloat_round_near_even', + 1 => 'softfloat_round_minMag', + 2 => 'softfloat_round_min', + 3 => 'softfloat_round_max', + 4 => 'softfloat_round_near_maxMag', + 7 => nil # DYN - handled specially + }.freeze + def initialize(emitter, mapping = {}) @emitter = emitter @mapping = mapping @@ -21,8 +35,29 @@ def binary_operation(emitter, operation, op_str) src2 = src2.nil? ? operation[:oprnds][2][:value] : src2 emitter.emit_line("#{dst} = #{src1} #{op_str} #{src2};") + end + + # Emit code to set SoftFloat rounding mode before FP operation + def emit_rm_setup(rm, tmp_var = '_rm_save') + return unless rm # nil means no rm handling needed + + if rm == 7 # DYN mode - read from fcsr at runtime + @emitter.emit_line('assert(0 && "CSR is currently not supported\n");') + @emitter.emit_line("uint_fast8_t #{tmp_var} = softfloat_roundingMode;") + # @emitter.emit_line('softfloat_roundingMode = cpu.getFCSR_RM();') + else + rm_const = SOFTFLOAT_RM_MAP[rm] + @emitter.emit_line("uint_fast8_t #{tmp_var} = softfloat_roundingMode;") + @emitter.emit_line("softfloat_roundingMode = #{rm_const};") + end + end - def emit_fp_binary(opname, operation, dst_type:, src_types:) + # Emit code to restore SoftFloat rounding mode after FP operation + def emit_rm_restore(tmp_var = '_rm_save') + @emitter.emit_line("softfloat_roundingMode = #{tmp_var};") + end + + def emit_fp_binary(opname, operation, dst_type:, src_types:, rm: nil) dst = map_operand(operation[:oprnds][0]) src1 = map_operand(operation[:oprnds][1]) src2 = map_operand(operation[:oprnds][2]) @@ -47,6 +82,9 @@ def emit_fp_binary(opname, operation, dst_type:, src_types:) @emitter.emit_line("#{ctype} #{t2} = (#{ctype})(#{src2});") end + # Set rounding mode before operation if rm is specified + emit_rm_setup(rm) if rm + if Utility::FP_INFO[dst_type] ctype = Utility::FP_INFO[dst_type][:c_type] @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{t1}, #{t2});") @@ -57,6 +95,81 @@ def emit_fp_binary(opname, operation, dst_type:, src_types:) @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{t1}, #{t2});") @emitter.emit_line("#{dst} = (uint64_t)#{tr};") end + + # Restore rounding mode after operation if rm was set + emit_rm_restore if rm + end + + # -> int conversions need to be handled separetely, because + # they break general pattern of softfloat instructions by demanding + # rounding mode as an argument (they ignore global constant that all other + # fp functions use for some unknown reason). + def emit_fp_to_int_conv(opname, operation, dst_type:, src_type:, rounding_mode:, exact: true) + dst = map_operand(operation[:oprnds][0]) + src = map_operand(operation[:oprnds][1]) + + ts = Utility.gen_typed_tmp(src, src_type) + tr = Utility.gen_typed_tmp(dst, dst_type) + + if Utility::FP_INFO[src_type] + info = Utility::FP_INFO[src_type] + @emitter.emit_line("#{info[:c_type]} #{ts} = { #{info[:unpack]}(#{src}) };") + else + ctype = Utility::INT_INFO[src_type] + @emitter.emit_line("#{ctype} #{ts} = (#{ctype})(#{src});") + end + + exact_arg = exact ? '1' : '0' + @emitter.emit_line("#{Utility::INT_INFO[dst_type]} #{tr} = #{opname}(#{ts}, #{rounding_mode}, #{exact_arg});") + @emitter.emit_line("#{dst} = (uint64_t)#{tr};") + end + + # There is no min/max/neg functions in softfloat, + # so we have to implement them ourselves + def emit_fp_min_max(opname, operation, type:) + dst, src1, src2 = map_n_operands(operation, 3) + t1 = Utility.gen_typed_tmp(src1, type) + t2 = Utility.gen_typed_tmp(src2, type) + tr = Utility.gen_typed_tmp(dst, type) + info = Utility::FP_INFO[type] + prefix = type == :f32 ? 'f32' : 'f64' + sign_bit = type == :f32 ? 31 : 63 + + @emitter.emit_line("#{info[:c_type]} #{t1} = { #{info[:unpack]}(#{src1}) };") + @emitter.emit_line("#{info[:c_type]} #{t2} = { #{info[:unpack]}(#{src2}) };") + @emitter.emit_line("#{info[:c_type]} #{tr};") + + # Bitwise NaN detection. Couldn't find softfloat's one, also this would faster with JIT + # in comparison to function call. + if type == :f32 + @emitter.emit_line("bool _a_nan = ((#{t1}.v >> 23) & 0xFF) == 0xFF && (#{t1}.v & 0x7FFFFF) != 0;") + @emitter.emit_line("bool _b_nan = ((#{t2}.v >> 23) & 0xFF) == 0xFF && (#{t2}.v & 0x7FFFFF) != 0;") + else + @emitter.emit_line("bool _a_nan = ((#{t1}.v >> 52) & 0x7FF) == 0x7FF && (#{t1}.v & 0xFFFFFFFFFFFFF) != 0;") + @emitter.emit_line("bool _b_nan = ((#{t2}.v >> 52) & 0x7FF) == 0x7FF && (#{t2}.v & 0xFFFFFFFFFFFFF) != 0;") + end + + is_min = opname.include?('min') + + @emitter.emit_line('{') + # return number if one of them is NaN, if both are NaN, return second + @emitter.emit_line(" if (_a_nan && _b_nan) #{tr} = #{t2};") + @emitter.emit_line(" else if (_a_nan) #{tr} = #{t2};") + @emitter.emit_line(" else if (_b_nan) #{tr} = #{t1};") + @emitter.emit_line(' else {') + if is_min + # smaller or neg zero + @emitter.emit_line(" if (#{prefix}_lt(#{t1}, #{t2}) || (#{prefix}_eq(#{t1}, #{t2}) && ((#{t1}.v >> #{sign_bit}) & 1))) #{tr} = #{t1};") + else + # larger or pos zero + @emitter.emit_line(" if (#{prefix}_lt(#{t2}, #{t1}) || (#{prefix}_eq(#{t1}, #{t2}) && ((#{t2}.v >> #{sign_bit}) & 1))) #{tr} = #{t1};") + end + @emitter.emit_line(" else #{tr} = #{t2};") + @emitter.emit_line(' }') + @emitter.emit_line('}') + + pack = info[:pack] + @emitter.emit_line("#{dst} = #{pack % tr};") end def gen_fp_neg(tmp_name, type) @@ -66,7 +179,7 @@ def gen_fp_neg(tmp_name, type) end end - def emit_fp_unary(opname, operation, dst_type:, src_type:) + def emit_fp_unary(opname, operation, dst_type:, src_type:, rm: nil) dst = map_operand(operation[:oprnds][0]) src = map_operand(operation[:oprnds][1]) @@ -81,6 +194,9 @@ def emit_fp_unary(opname, operation, dst_type:, src_type:) @emitter.emit_line("#{ctype} #{ts} = (#{ctype})(#{src});") end + # Set rounding mode before operation if rm is specified + emit_rm_setup(rm) if rm + if Utility::FP_INFO[dst_type] ctype = Utility::FP_INFO[dst_type][:c_type] @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{ts});") @@ -91,12 +207,16 @@ def emit_fp_unary(opname, operation, dst_type:, src_type:) @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{ts});") @emitter.emit_line("#{dst} = (uint64_t)#{tr};") end + + # Restore rounding mode after operation if rm was set + emit_rm_restore if rm end def emit_fp_ternary(opname, operation, dst_type:, src_types:, - negate_src: []) + negate_src: [], + rm: nil) dst = map_operand(operation[:oprnds][0]) src1 = map_operand(operation[:oprnds][1]) src2 = map_operand(operation[:oprnds][2]) @@ -114,8 +234,11 @@ def emit_fp_ternary(opname, operation, info = Utility::FP_INFO[ty] @emitter.emit_line("#{info[:c_type]} #{t} = { #{info[:unpack]}(#{src}) };") if negate_src.include?(i) - negf = ty == :f32 ? 'f32_neg' : 'f64_neg' - @emitter.emit_line("#{t} = #{negf}(#{t});") + # TODO: change for gen_fp_neg + negation = gen_fp_neg(t, ty) + @emitter.emit_line(negation) + # negf = ty == :f32 ? 'f32_neg' : 'f64_neg' + # @emitter.emit_line("#{t} = #{negf}(#{t});") end else @@ -127,6 +250,10 @@ def emit_fp_ternary(opname, operation, end tr = Utility.gen_typed_tmp(dst, dst_type) + + # Set rounding mode before operation if rm is specified + emit_rm_setup(rm) if rm + if Utility::FP_INFO[dst_type] ctype = Utility::FP_INFO[dst_type][:c_type] @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{svars.join(', ')});") @@ -137,6 +264,9 @@ def emit_fp_ternary(opname, operation, @emitter.emit_line("#{ctype} #{tr} = #{opname}(#{svars.join(', ')});") @emitter.emit_line("#{dst} = (uint64_t)#{tr};") end + + # Restore rounding mode after operation if rm was set + emit_rm_restore if rm end def emit_sign_inject(operation, width:, mode:) @@ -201,6 +331,9 @@ def cpu_read_mem(dst, addr) end def generate_statement(operation) + # Extract rm from attrs if present + rm = operation[:attrs] if operation[:attrs].is_a?(Integer) + case operation[:name] when :add binary_operation(@emitter, operation, '+') @@ -293,62 +426,103 @@ def generate_statement(operation) @emitter.emit_line("#{dst} = #{cond} ? #{true_val} : #{false_val};") - # Floating point arithmetic operations + # Floating point arithmetic operations WITH rounding mode (SoftFloat global) when :f32_add then emit_fp_binary('f32_add', operation, dst_type: :f32, - src_types: %i[f32 f32]) + src_types: %i[f32 f32], + rm: rm) when :f64_add then emit_fp_binary('f64_add', operation, dst_type: :f64, - src_types: %i[f64 f64]) + src_types: %i[f64 f64], + rm: rm) when :f32_sub then emit_fp_binary('f32_sub', operation, dst_type: :f32, - src_types: %i[f32 f32]) + src_types: %i[f32 f32], + rm: rm) when :f64_sub then emit_fp_binary('f64_sub', operation, dst_type: :f64, - src_types: %i[f64 f64]) + src_types: %i[f64 f64], + rm: rm) when :f32_mul then emit_fp_binary('f32_mul', operation, dst_type: :f32, - src_types: %i[f32 f32]) + src_types: %i[f32 f32], + rm: rm) when :f64_mul then emit_fp_binary('f64_mul', operation, dst_type: :f64, - src_types: %i[f64 f64]) + src_types: %i[f64 f64], + rm: rm) when :f32_div then emit_fp_binary('f32_div', operation, dst_type: :f32, - src_types: %i[f32 f32]) + src_types: %i[f32 f32], + rm: rm) when :f64_div then emit_fp_binary('f64_div', operation, dst_type: :f64, - src_types: %i[f64 f64]) + src_types: %i[f64 f64], + rm: rm) when :f32_sqrt then emit_fp_unary('f32_sqrt', operation, dst_type: :f32, - src_type: :f32) + src_type: :f32, + rm: rm) when :f64_sqrt then emit_fp_unary('f64_sqrt', operation, dst_type: :f64, - src_type: :f64) + src_type: :f64, + rm: rm) when :f32_mul_add then emit_fp_ternary('f32_mulAdd', operation, dst_type: :f32, - src_types: %i[f32 f32 f32]) + src_types: %i[f32 f32 f32], + rm: rm) when :f64_mul_add then emit_fp_ternary('f64_mulAdd', operation, dst_type: :f64, - src_types: %i[f64 f64 f64]) - when :f64_mul_sub - emit_fp_ternary('f64_mulAdd', operation, - dst_type: :f64, - src_types: %i[f64 f64 f64], - negate_src: [3]) - when :f64_mul_sub_n - emit_fp_ternary('f64_mulAdd', operation, - dst_type: :f64, - src_types: %i[f64 f64 f64], - negate_src: [1, 3]) + src_types: %i[f64 f64 f64], + rm: rm) + + # Fused Multiply-Add with Negation + when :f32_mul_add_n then emit_fp_ternary('f32_mulAdd', operation, + dst_type: :f32, + src_types: %i[f32 f32 f32], + negate_src: [0], + rm: rm) + + when :f64_mul_add_n then emit_fp_ternary('f64_mulAdd', operation, + dst_type: :f64, + src_types: %i[f64 f64 f64], + negate_src: [0], + rm: rm) + + when :f32_mul_sub then emit_fp_ternary('f32_mulAdd', operation, + dst_type: :f32, + src_types: %i[f32 f32 f32], + negate_src: [2], + rm: rm) + + when :f64_mul_sub then emit_fp_ternary('f64_mulAdd', operation, + dst_type: :f64, + src_types: %i[f64 f64 f64], + negate_src: [2], + rm: rm) + + when :f32_mul_sub_n then emit_fp_ternary('f32_mulAdd', operation, + dst_type: :f32, + src_types: %i[f32 f32 f32], + negate_src: [0, 2], + rm: rm) + + when :f64_mul_sub_n then emit_fp_ternary('f64_mulAdd', operation, + dst_type: :f64, + src_types: %i[f64 f64 f64], + negate_src: [0, 2], + rm: rm) + + # Floating point comparisons (NO rounding mode - SoftFloat doesn't need it) when :f32_eq then emit_fp_binary('f32_eq', operation, dst_type: :i32, src_types: %i[f32 f32]) @@ -367,26 +541,11 @@ def generate_statement(operation) when :f64_le then emit_fp_binary('f64_le', operation, dst_type: :i32, src_types: %i[f64 f64]) - when :f32_min then emit_fp_binary('f32_min', operation, - dst_type: :f32, - src_types: %i[f32 f32]) - when :f64_min then emit_fp_binary('f64_min', operation, - dst_type: :f64, - src_types: %i[f64 f64]) - when :f32_max then emit_fp_binary('f32_max', operation, - dst_type: :f32, - src_types: %i[f32 f32]) - when :f64_max then emit_fp_binary('f64_max', operation, - dst_type: :f64, - src_types: %i[f64 f64]) - when :f32_mul_sub then emit_fp_ternary('f32_mulAdd', operation, - dst_type: :f32, - src_types: %i[f32 f32 f32], - negate_src: [2]) - when :f32_mul_sub_n then emit_fp_ternary('f32_mulAdd', operation, - dst_type: :f32, - src_types: %i[f32 f32 f32], - negate_src: [0, 2]) + + when :f32_min then emit_fp_min_max('f32_min', operation, type: :f32) + when :f64_min then emit_fp_min_max('f64_min', operation, type: :f64) + when :f32_max then emit_fp_min_max('f32_max', operation, type: :f32) + when :f64_max then emit_fp_min_max('f64_max', operation, type: :f64) # Floating point injections when :f32_sign_injection then emit_sign_inject(operation, width: 32, mode: :copy) @@ -397,30 +556,29 @@ def generate_statement(operation) when :f32_sign_xor then emit_sign_inject(operation, width: 32, mode: :xor) when :f64_sign_xor then emit_sign_inject(operation, width: 64, mode: :xor) - # Floating point conversions - when :f32_to_i32 then emit_fp_unary('f32_to_i32', operation, - dst_type: :i32, src_type: :f32) - - when :f32_to_u32 then emit_fp_unary('f32_to_ui32', operation, - dst_type: :u32, src_type: :f32) - when :f32_to_i64 then emit_fp_unary('f32_to_i64', operation, - dst_type: :i64, src_type: :f32) - - when :f32_to_u64 then emit_fp_unary('f32_to_ui64', operation, - dst_type: :u64, src_type: :f32) + # Floating point conversions WITH rounding mode (SoftFloat global) + when :f32_to_i32 then emit_fp_to_int_conv('f32_to_i32', operation, dst_type: :i32, src_type: :f32, + rounding_mode: rm) + when :f32_to_u32 then emit_fp_to_int_conv('f32_to_ui32', operation, dst_type: :u32, src_type: :f32, + rounding_mode: rm) + when :f32_to_i64 then emit_fp_to_int_conv('f32_to_i64', operation, dst_type: :i64, src_type: :f32, + rounding_mode: rm) + when :f32_to_u64 then emit_fp_to_int_conv('f32_to_ui64', operation, dst_type: :u64, src_type: :f32, + rounding_mode: rm) when :i32_to_f32 then emit_fp_unary('i32_to_f32', operation, - dst_type: :f32, src_type: :i32) + dst_type: :f32, src_type: :i32, rm: rm) when :u32_to_f32 then emit_fp_unary('ui32_to_f32', operation, - dst_type: :f32, src_type: :u32) + dst_type: :f32, src_type: :u32, rm: rm) when :i64_to_f32 then emit_fp_unary('i64_to_f32', operation, - dst_type: :f32, src_type: :i64) + dst_type: :f32, src_type: :i64, rm: rm) when :u64_to_f32 then emit_fp_unary('ui64_to_f32', operation, - dst_type: :f32, src_type: :u64) + dst_type: :f32, src_type: :u64, rm: rm) + # Classification when :f32_classify dst, src = map_n_operands(operation, 2) @@ -469,7 +627,7 @@ def generate_statement(operation) @emitter.emit_line("#{dst} = r;") @emitter.emit_line('}') - else raise 'Unknown statement type, terminating program' + else raise "Unknown statement type: #{operation[:name]}, terminating program" end end end diff --git a/lib/ADL/scope.rb b/lib/ADL/scope.rb index 60dcb31..6a1b110 100644 --- a/lib/ADL/scope.rb +++ b/lib/ADL/scope.rb @@ -59,83 +59,90 @@ def resolve_const(what) Constant.new(self, "const_#{next_counter}", what) if what.class == Integer end - def binOp(a, b, op) + # SUGGESTION: + # Make those helpers accept optional attrs argument, + # which is now used to pass rounding mode for some fp instructions. + # Other pseudo-operands may appear in other RV modules, + # so it is a necessary addition imo. + def binOp(a, b, op, attrs = nil) binOpWType(a, b, op, - Utility.get_type(a.type).typeof == :r ? ('b' + Utility.get_type(a.type).bitsize.to_s).to_sym : a.type) + Utility.get_type(a.type).typeof == :r ? ('b' + Utility.get_type(a.type).bitsize.to_s).to_sym : a.type, + attrs) end - def binOpWType(a, b, op, t) + def binOpWType(a, b, op, t, attrs = nil) a = resolve_const(a) b = resolve_const(b) # TODO: check constant size <= bitsize(var) # assert(a.type== b.type|| a.type == :iconst || b.type== :iconst) - stmt op, [tmpvar(t), a, b] + stmt op, [tmpvar(t), a, b], attrs end def getOpType(a) Utility.get_type(a.type).typeof == :r ? ('b' + Utility.get_type(a.type).bitsize.to_s).to_sym : a.type end - def unOp(a, op) - unOpWType(a, op, getOpType(a)) + def unOp(a, op, attrs = nil) + unOpWType(a, op, getOpType(a), attrs) end - def unOpWType(a, op, t) + def unOpWType(a, op, t, attrs = nil) a = resolve_const(a) - stmt op, [tmpvar(t), a] + stmt op, [tmpvar(t), a], attrs end - def ternOp(a, b, c, op) - ternOpWType(a, b, c, op, getOpType(a)) + def ternOp(a, b, c, op, attrs = nil) + ternOpWType(a, b, c, op, getOpType(a), attrs) end - def ternOpWType(a, b, c, op, t) + def ternOpWType(a, b, c, op, t, attrs = nil) a = resolve_const(a) b = resolve_const(b) c = resolve_const(c) # TODO: check constant size <= bitsize(var) # assert(a.type== b.type|| a.type == :iconst || b.type== :iconst) - stmt op, [tmpvar(t), a, b, c] + stmt op, [tmpvar(t), a, b, c], attrs end - # redefine! add & sub will never be the same - def add(a, b) = binOp(a, b, :add) - def sub(a, b) = binOp(a, b, :sub) - def shl(a, b) = binOp(a, b, :shl) - def lt(a, b) = binOpWType(a, b, :lt, :b1) - def gt(a, b) = binOpWType(a, b, :gt, :b1) - def le(a, b) = binOpWType(a, b, :le, :b1) - def ge(a, b) = binOpWType(a, b, :ge, :b1) - def xor(a, b) = binOp(a, b, :xor) - def shr(a, b) = binOp(a, b, :shr) - def ashr(a, b) = binOp(a, b, :ashr) - def or(a, b) = binOp(a, b, :or) - def and(a, b) = binOp(a, b, :and) - def eq(a, b) = binOpWType(a, b, :eq, :b1) - def ne(a, b) = binOpWType(a, b, :ne, :b1) - - # Floating point operations - def f32_add(a, b) = binOp(a, b, :f32_add) - def f64_add(a, b) = binOp(a, b, :f64_add) - def f32_sub(a, b) = binOp(a, b, :f32_sub) - def f64_sub(a, b) = binOp(a, b, :f64_sub) - def f32_mul(a, b) = binOp(a, b, :f32_mul) - def f64_mul(a, b) = binOp(a, b, :f64_mul) - def f32_div(a, b) = binOp(a, b, :f32_div) - def f64_div(a, b) = binOp(a, b, :f64_div) - - def f32_sqrt(a) = unOp(a, :f32_sqrt) - def f64_sqrt(a) = unOp(a, :f64_sqrt) - - def f32_mul_add(a, b, c) = ternOp(a, b, c, :f32_mul_add) - def f64_mul_add(a, b, c) = ternOp(a, b, c, :f64_mul_add) - def f32_mul_sub(a, b, c) = ternOp(a, b, c, :f32_mul_sub) - def f64_mul_sub(a, b, c) = ternOp(a, b, c, :f64_mul_sub) - def f32_mul_add_n(a, b, c) = ternOp(a, b, c, :f32_mul_sub_n) - def f64_mul_add_n(a, b, c) = ternOp(a, b, c, :f64_mul_sub_n) - def f32_mul_sub_n(a, b, c) = ternOp(a, b, c, :f32_mul_sub_n) - def f64_mul_sub_n(a, b, c) = ternOp(a, b, c, :f64_mul_sub_n) - + # Integer arithmetic + def add(a, b, attrs = nil) = binOp(a, b, :add, attrs) + def sub(a, b, attrs = nil) = binOp(a, b, :sub, attrs) + def shl(a, b, attrs = nil) = binOp(a, b, :shl, attrs) + def lt(a, b, attrs = nil) = binOpWType(a, b, :lt, :b1, attrs) + def gt(a, b, attrs = nil) = binOpWType(a, b, :gt, :b1, attrs) + def le(a, b, attrs = nil) = binOpWType(a, b, :le, :b1, attrs) + def ge(a, b, attrs = nil) = binOpWType(a, b, :ge, :b1, attrs) + def xor(a, b, attrs = nil) = binOp(a, b, :xor, attrs) + def shr(a, b, attrs = nil) = binOp(a, b, :shr, attrs) + def ashr(a, b, attrs = nil) = binOp(a, b, :ashr, attrs) + def or(a, b, attrs = nil) = binOp(a, b, :or, attrs) + def and(a, b, attrs = nil) = binOp(a, b, :and, attrs) + def eq(a, b, attrs = nil) = binOpWType(a, b, :eq, :b1, attrs) + def ne(a, b, attrs = nil) = binOpWType(a, b, :ne, :b1, attrs) + + # Floating point Arithmetic + def f32_add(a, b, rm = nil) = binOp(a, b, :f32_add, rm) + def f64_add(a, b, rm = nil) = binOp(a, b, :f64_add, rm) + def f32_sub(a, b, rm = nil) = binOp(a, b, :f32_sub, rm) + def f64_sub(a, b, rm = nil) = binOp(a, b, :f64_sub, rm) + def f32_mul(a, b, rm = nil) = binOp(a, b, :f32_mul, rm) + def f64_mul(a, b, rm = nil) = binOp(a, b, :f64_mul, rm) + def f32_div(a, b, rm = nil) = binOp(a, b, :f32_div, rm) + def f64_div(a, b, rm = nil) = binOp(a, b, :f64_div, rm) + + def f32_sqrt(a, rm = nil) = unOp(a, :f32_sqrt, rm) + def f64_sqrt(a, rm = nil) = unOp(a, :f64_sqrt, rm) + + def f32_mul_add(a, b, c, rm = nil) = ternOp(a, b, c, :f32_mul_add, rm) + def f64_mul_add(a, b, c, rm = nil) = ternOp(a, b, c, :f64_mul_add, rm) + def f32_mul_sub(a, b, c, rm = nil) = ternOp(a, b, c, :f32_mul_sub, rm) + def f64_mul_sub(a, b, c, rm = nil) = ternOp(a, b, c, :f64_mul_sub, rm) + def f32_mul_add_n(a, b, c, rm = nil) = ternOp(a, b, c, :f32_mul_add_n, rm) + def f64_mul_add_n(a, b, c, rm = nil) = ternOp(a, b, c, :f64_mul_add_n, rm) + def f32_mul_sub_n(a, b, c, rm = nil) = ternOp(a, b, c, :f32_mul_sub_n, rm) + def f64_mul_sub_n(a, b, c, rm = nil) = ternOp(a, b, c, :f64_mul_sub_n, rm) + + # Minmax, sign injection def f32_min(a, b) = binOp(a, b, :f32_min) def f64_min(a, b) = binOp(a, b, :f64_min) def f32_max(a, b) = binOp(a, b, :f32_max) @@ -153,15 +160,17 @@ def f64_sign_injection_n(a, b) = binOp(a, b, :f64_sign_injection_n) def f32_sign_xor(a, b) = binOp(a, b, :f32_sign_xor) def f64_sign_xor(a, b) = binOp(a, b, :f64_sign_xor) - # TODO: unary op - def f32_to_i32(a) = unOp(a, :f32_to_i32) - def f32_to_u32(a) = unOp(a, :f32_to_u32) - def f32_to_i64(a) = unOp(a, :f32_to_i64) - def f32_to_u64(a) = unOp(a, :f32_to_u64) - def i32_to_f32(a) = unOp(a, :i32_to_f32) - def u32_to_f32(a) = unOp(a, :u32_to_f32) - def i64_to_f32(a) = unOp(a, :i64_to_f32) - def u64_to_f32(a) = unOp(a, :u64_to_f32) + # Conversion + def f32_to_i32(a, rm = nil) = unOp(a, :f32_to_i32, rm) + def f32_to_u32(a, rm = nil) = unOp(a, :f32_to_u32, rm) + def f32_to_i64(a, rm = nil) = unOp(a, :f32_to_i64, rm) + def f32_to_u64(a, rm = nil) = unOp(a, :f32_to_u64, rm) + def i32_to_f32(a, rm = nil) = unOp(a, :i32_to_f32, rm) + def u32_to_f32(a, rm = nil) = unOp(a, :u32_to_f32, rm) + def i64_to_f32(a, rm = nil) = unOp(a, :i64_to_f32, rm) + def u64_to_f32(a, rm = nil) = unOp(a, :u64_to_f32, rm) + + # Classification def f32_classify(a) = unOp(a, :f32_classify) def f64_classify(a) = unOp(a, :f64_classify) @@ -230,7 +239,7 @@ def arlet(sym, regset, attrs, type, expr) def branch(expr) = stmt(:branch, [expr]) private def tmpvar(type) = var("_tmp#{next_counter}".to_sym, type) - # stmtadds statement into tree and retursoperand[0] + # stmt adds statement into tree and returns operand[0] # which result in near all cases def stmt(name, operands, attrs = nil) for i in 1...operands.length diff --git a/lib/Target/RISC-V/64F.rb b/lib/Target/RISC-V/64F.rb index 7772aae..a0bba6b 100644 --- a/lib/Target/RISC-V/64F.rb +++ b/lib/Target/RISC-V/64F.rb @@ -5,6 +5,7 @@ module RV64F include SimInfra extend SimInfra + # NOTE: semantics now are just dummies, they will change in future # Register float regs in regfile RegisterFile(:FRegs) do @@ -13,88 +14,102 @@ module RV64F end end + RV_64_F_ROUNDING_MODES = { RNE: 0b000, RTZ: 0b001, RDN: 0b010, RUP: 0b011, + RMM: 0b100, DYN: 0b111 }.freeze + # Generic helper for any FP instruction with rounding mode + # Usage: fp_inst(:fadd_d) { |rm| encoding(...); asm {...}; code {...} } + def self.fp_inst(name, &block) + module_name = :RV64F + RV_64_F_ROUNDING_MODES.each do |rm_name, rm_value| + inst_name = :"#{name}_#{rm_name.to_s.downcase}" + bldr = InstructionInfoBuilder.new(inst_name, module_name) + bldr.instance_exec(rm_value, &block) + @@instructions << bldr.info + end + end + # Basic arithmetic - Instruction(:fadd_s) do - encoding(*format_r_fp(0b1010011, 0b0000000)) + fp_inst(:fadd_s) do |rm| + encoding(*format_r_fp(0b1010011, 0b0000000, rm)) asm { 'fadd.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_add(frs1, frs2) + frd[] = f32_add(frs1, frs2, rm) end end - Instruction(:fadd_d) do - encoding(*format_r_fp(0b1010011, 0b0000001)) + fp_inst(:fadd_d) do |rm| + encoding(*format_r_fp(0b1010011, 0b0000001, rm)) asm { 'fadd.d {frd}, {frs1}, {frs2}' } code do - frd[] = f64_add(frs1, frs2) + frd[] = f64_add(frs1, frs2, rm) end end - Instruction(:fsub_s) do - encoding(*format_r_fp(0b1010011, 0b0000100)) + fp_inst(:fsub_s) do |rm| + encoding(*format_r_fp(0b1010011, 0b0000100, rm)) asm { 'fsub.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_sub(frs1, frs2) + frd[] = f32_sub(frs1, frs2, rm) end end - Instruction(:fsub_d) do - encoding(*format_r_fp(0b1010011, 0b0000101)) + fp_inst(:fsub_d) do |rm| + encoding(*format_r_fp(0b1010011, 0b0000101, rm)) asm { 'fsub.d {frd}, {frs1}, {frs2}' } code do - frd[] = f64_sub(frs1, frs2) + frd[] = f64_sub(frs1, frs2, rm) end end - Instruction(:fmul_s) do - encoding(*format_r_fp(0b1010011, 0b0001000)) + fp_inst(:fmul_s) do |rm| + encoding(*format_r_fp(0b1010011, 0b0001000, rm)) asm { 'fmul.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_mul(frs1, frs2) + frd[] = f32_mul(frs1, frs2, rm) end end - Instruction(:fmul_d) do - encoding(*format_r_fp(0b1010011, 0b0001001)) + fp_inst(:fmul_d) do |rm| + encoding(*format_r_fp(0b1010011, 0b0001001, rm)) asm { 'fmul.d {frd}, {frs1}, {frs2}' } code do - frd[] = f64_mul(frs1, frs2) + frd[] = f64_mul(frs1, frs2, rm) end end - Instruction(:fdiv_s) do - encoding(*format_r_fp(0b1010011, 0b0001100)) + fp_inst(:fdiv_s) do |rm| + encoding(*format_r_fp(0b1010011, 0b0001100, rm)) asm { 'fdiv.s {frd}, {frs1}, {frs2}' } code do - frd[] = f32_div(frs1, frs2) + frd[] = f32_div(frs1, frs2, rm) end end - Instruction(:fdiv_d) do - encoding(*format_r_fp(0b1010011, 0b0001101)) + fp_inst(:fdiv_d) do |rm| + encoding(*format_r_fp(0b1010011, 0b0001101, rm)) asm { 'fdiv.d {frd}, {frs1}, {frs2}' } code do - frd[] = f64_div(frs1, frs2) + frd[] = f64_div(frs1, frs2, rm) end end - Instruction(:fsqrt_s) do - encoding(*format_r_fp(0b1010011, 0b0101100)) + fp_inst(:fsqrt_s) do |rm| + encoding(*format_r_fp(0b1010011, 0b0101100, rm)) asm { 'fsqrt.s {frd}, {frs1}' } code do - frd[] = f32_sqrt(frs1) + frd[] = f32_sqrt(frs1, rm) end end - Instruction(:fsqrt_d) do - encoding(*format_r_fp(0b1010011, 0b0101101)) + fp_inst(:fsqrt_d) do |rm| + encoding(*format_r_fp(0b1010011, 0b0101101, rm)) asm { 'fsqrt.d {frd}, {frs1}' } code do - frd[] = f64_sqrt(frs1) + frd[] = f64_sqrt(frs1, rm) end end - # Memory + # Memory (no rm field - unchanged) Instruction(:flw) do encoding(*format_i_fp(0b0000111, 0b010)) asm { 'flw {frd}, {imm}({rs1})' } @@ -128,71 +143,71 @@ module RV64F end # Fused Mul/Add - Instruction(:fmadd_s) do - encoding(*format_r4_fp(0b1000011, 0b0)) + fp_inst(:fmadd_s) do |rm| + encoding(*format_r4_fp(0b1000011, 0b00, rm)) asm { 'fmadd.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f32_mul_add(frs1, frs2, frs3) + frd[] = f32_mul_add(frs1, frs2, frs3, rm) end end - Instruction(:fmadd_d) do - encoding(*format_r4_fp(0b1000011, 0b01)) + fp_inst(:fmadd_d) do |rm| + encoding(*format_r4_fp(0b1000011, 0b01, rm)) asm { 'fmadd.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f64_mul_add(frs1, frs2, frs3) + frd[] = f64_mul_add(frs1, frs2, frs3, rm) end end - Instruction(:fmsub_s) do - encoding(*format_r4_fp(0b1000111, 0b0)) + fp_inst(:fmsub_s) do |rm| + encoding(*format_r4_fp(0b1000111, 0b00, rm)) asm { 'fmsub.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f32_mul_sub(frs1, frs2, frs3) + frd[] = f32_mul_sub(frs1, frs2, frs3, rm) end end - Instruction(:fmsub_d) do - encoding(*format_r4_fp(0b1000111, 0b01)) + fp_inst(:fmsub_d) do |rm| + encoding(*format_r4_fp(0b1000111, 0b01, rm)) asm { 'fmsub.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f64_mul_sub(frs1, frs2, frs3) + frd[] = f64_mul_sub(frs1, frs2, frs3, rm) end end - Instruction(:fnmadd_s) do - encoding(*format_r4_fp(0b1001111, 0b00)) + fp_inst(:fnmadd_s) do |rm| + encoding(*format_r4_fp(0b1001111, 0b00, rm)) asm { 'fnmadd.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f32_mul_add_n(frs1, frs2, frs3) + frd[] = f32_mul_add_n(frs1, frs2, frs3, rm) end end - Instruction(:fnmadd_d) do - encoding(*format_r4_fp(0b1001111, 0b01)) + fp_inst(:fnmadd_d) do |rm| + encoding(*format_r4_fp(0b1001111, 0b01, rm)) asm { 'fnmadd.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f64_mul_add_n(frs1, frs2, frs3) + frd[] = f64_mul_add_n(frs1, frs2, frs3, rm) end end - Instruction(:fnmsub_s) do - encoding(*format_r4_fp(0b1001011, 0b00)) + fp_inst(:fnmsub_s) do |rm| + encoding(*format_r4_fp(0b1001011, 0b00, rm)) asm { 'fnmsub.s {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f32_mul_sub_n(frs1, frs2, frs3) + frd[] = f32_mul_sub_n(frs1, frs2, frs3, rm) end end - Instruction(:fnmsub_d) do - encoding(*format_r4_fp(0b1001011, 0b01)) + fp_inst(:fnmsub_d) do |rm| + encoding(*format_r4_fp(0b1001011, 0b01, rm)) asm { 'fnmsub.d {frd}, {frs1}, {frs2}, {frs3}' } code do - frd[] = f64_mul_sub_n(frs1, frs2, frs3) + frd[] = f64_mul_sub_n(frs1, frs2, frs3, rm) end end - # Sign injection + # Sign injection (no rm field - unchanged) Instruction(:fsgnj_s) do encoding(*format_r_fp_no_rm(0b1010011, 0b000, 0b0010000)) asm { 'fsgnj.s {frd}, {frs1}, {frs2}' } @@ -241,7 +256,7 @@ module RV64F end end - # Comparisons + # Comparisons (no rm field - unchanged) Instruction(:fmin_s) do encoding(*format_r_fp_no_rm(0b1010011, 0b000, 0b0010100)) asm { 'fmin.s {frd}, {frs1}, {frs2}' } @@ -323,71 +338,71 @@ module RV64F end # Conversions - Instruction(:fcvt_w_s) do - encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00000, 0b1100000)) + fp_inst(:fcvt_w_s) do |rm| + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00000, 0b1100000, rm)) asm { 'fcvt.w.s {rd}, {frs1}' } code do - rd[] = f32_to_i32(frs1) + rd[] = f32_to_i32(frs1, rm) end end - Instruction(:fcvt_wu_s) do - encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00001, 0b1100000)) + fp_inst(:fcvt_wu_s) do |rm| + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00001, 0b1100000, rm)) asm { 'fcvt.wu.s {rd}, {frs1}' } code do - rd[] = f32_to_u32(frs1) + rd[] = f32_to_u32(frs1, rm) end end - Instruction(:fcvt_l_s) do - encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00010, 0b1100000)) + fp_inst(:fcvt_l_s) do |rm| + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00010, 0b1100000, rm)) asm { 'fcvt.l.s {rd}, {frs1}' } code do - rd[] = f32_to_i64(frs1) + rd[] = f32_to_i64(frs1, rm) end end - Instruction(:fcvt_lu_s) do - encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00011, 0b1100000)) + fp_inst(:fcvt_lu_s) do |rm| + encoding(*format_r_fp_fcvt_rd(0b1010011, 0b00011, 0b1100000, rm)) asm { 'fcvt.lu.s {rd}, {frs1}' } code do - rd[] = f32_to_u64(frs1) + rd[] = f32_to_u64(frs1, rm) end end - Instruction(:fcvt_s_w) do - encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00000, 0b1101000)) + fp_inst(:fcvt_s_w) do |rm| + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00000, 0b1101000, rm)) asm { 'fcvt.s.w {frd}, {rs1}' } code do - frd[] = i32_to_f32(rs1) + frd[] = i32_to_f32(rs1, rm) end end - Instruction(:fcvt_s_wu) do - encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00001, 0b1101000)) + fp_inst(:fcvt_s_wu) do |rm| + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00001, 0b1101000, rm)) asm { 'fcvt.s.wu {frd}, {rs1}' } code do - frd[] = u32_to_f32(rs1) + frd[] = u32_to_f32(rs1, rm) end end - Instruction(:fcvt_s_l) do - encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00010, 0b1101000)) + fp_inst(:fcvt_s_l) do |rm| + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00010, 0b1101000, rm)) asm { 'fcvt.s.l {frd}, {rs1}' } code do - frd[] = i64_to_f32(rs1) + frd[] = i64_to_f32(rs1, rm) end end - Instruction(:fcvt_s_lu) do - encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00011, 0b1101000)) + fp_inst(:fcvt_s_lu) do |rm| + encoding(*format_r_fp_fcvt_frd(0b1010011, 0b00011, 0b1101000, rm)) asm { 'fcvt.s.lu {frd}, {rs1}' } code do - frd[] = u64_to_f32(rs1) + frd[] = u64_to_f32(rs1, rm) end end - # Classification + # Classification (no rm field - unchanged) Instruction(:fclass_s) do encoding(*format_r_fp_class(0b1010011, 0b001, 0b00000, 0b1110000)) asm { 'fclass.s {rd}, {frs1}' } @@ -404,7 +419,7 @@ module RV64F end end - # Move + # Move (no rm field - unchanged) Instruction(:fmv_x_w) do encoding(*format_r_fp_no_rm_move_rd(0b1010011, 0b000, 0b1110000)) asm { 'fmv.x.w {rd}, {frs1}' } diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index f9efd01..e32f4b4 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -31,7 +31,7 @@ def xreg(name) [name, :r32, "let :#{name}, :XRegs, [:op], :r32, f_#{name}"] end - def freg64(name) + def freg(name) [name, :f64, "let :#{name}, :FRegs, [:op], :f64, f_#{name}"] end end @@ -56,11 +56,11 @@ def format_r(opcode, funct3, funct7) ], xreg(:rs2), xreg(:rs1), xreg(:rd)] end - def format_r4_fp(opcode, funct2) + def format_r4_fp(opcode, funct2, rm) [:R4_FP, [ field(:f_opcode, 6, 0, opcode), field(:f_frd, 11, 7), - field(:f_rm, 14, 12), + field(:f_rm, 14, 12, rm), field(:f_frs1, 19, 15), field(:f_frs2, 24, 20), field(:f_frs3, 31, 27), @@ -68,11 +68,11 @@ def format_r4_fp(opcode, funct2) ], freg(:frs3), freg(:frs2), freg(:frs1), freg(:frd)] end - def format_r_fp(opcode, funct7) + def format_r_fp(opcode, funct7, rm) [:R_FP, [ field(:f_opcode, 6, 0, opcode), field(:f_frd, 11, 7), - field(:f_rm, 14, 12), + field(:f_rm, 14, 12, rm), field(:f_frs1, 19, 15), field(:f_frs2, 24, 20), field(:f_funct7, 31, 25, funct7) @@ -112,22 +112,22 @@ def format_r_fp_no_rm(opcode, funct3, funct7) ], freg(:frs2), freg(:frs1), freg(:frd)] end - def format_r_fp_fcvt_frd(opcode, funct5, funct7) + def format_r_fp_fcvt_frd(opcode, funct5, funct7, rm) [:R_FCVT, [ field(:f_opcode, 6, 0, opcode), field(:f_frd, 11, 7), - field(:f_rm, 14, 12), + field(:f_rm, 14, 12, rm), field(:f_rs1, 19, 15), field(:f_funct5, 24, 20, funct5), field(:f_funct7, 31, 25, funct7) ], xreg(:rs1), freg(:frd)] end - def format_r_fp_fcvt_rd(opcode, funct5, funct7) + def format_r_fp_fcvt_rd(opcode, funct5, funct7, rm) [:R_FCVT, [ field(:f_opcode, 6, 0, opcode), field(:f_rd, 11, 7), - field(:f_rm, 14, 12), + field(:f_rm, 14, 12, rm), field(:f_frs1, 19, 15), field(:f_funct5, 24, 20, funct5), field(:f_funct7, 31, 25, funct7) diff --git a/sim_gen/Decoders/decoder.rb b/sim_gen/Decoders/decoder.rb index d0cf5c1..ee429bf 100644 --- a/sim_gen/Decoders/decoder.rb +++ b/sim_gen/Decoders/decoder.rb @@ -7,6 +7,8 @@ module Decoder module Helper module_function + # SUGGESTION: add following comment for better readability + # Constant fields are identified by the non-nil value in their :value_num field. def calc_insn_mask(insn) mask = 0 for field in insn[:fields] From 041256b93d43d306dfdfd3eb9e6b9fc5b10962af Mon Sep 17 00:00:00 2001 From: doushe821 Date: Tue, 28 Apr 2026 20:25:27 +0300 Subject: [PATCH 7/8] [Tests][Decoder] Added unit tests for rv64f. Also fixed a small bug in decorder, also started to write proposals (getting ready for mr) --- CMakeLists.txt | 5 + cmake/dependencies.cmake | 14 ++ sim_gen/Decoders/decoder.rb | 16 +- sim_gen/ISA/isa.rb | 189 ++++++++--------- tests/CMakeLists.txt | 29 +++ tests/rv64f/rv64f_tests.cc | 410 ++++++++++++++++++++++++++++++++++++ 6 files changed, 566 insertions(+), 97 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/rv64f/rv64f_tests.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 97b6af6..04a60ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,14 @@ find_package(Ruby 3.4.8 EXACT REQUIRED) include(cmake/Bundler.cmake) include(cmake/CompilerOptions.cmake) include(cmake/CPM.cmake) +include(CTest) include(cmake/dependencies.cmake) include(cmake/softfloat.cmake) add_subdirectory(lib) add_subdirectory(sim_lib) add_subdirectory(sim_gen) + +if(BUILD_TESTING) + add_subdirectory(tests) +endif() diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 618e78f..e133004 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -21,3 +21,17 @@ CPMAddPackage( GIT_TAG 12.0.0 EXCLUDE_FROM_ALL True SYSTEM True) + +if(UNIT_TESTS) + # GoogleTest: C++ unit testing framework + CPMAddPackage( + NAME googletest + GITHUB_REPOSITORY google/googletest + GIT_TAG v1.17.0 + VERSION 1.17.0 + EXCLUDE_FROM_ALL True + SYSTEM True + OPTIONS + "INSTALL_GTEST OFF" + "gtest_force_shared_crt ON") +endif() diff --git a/sim_gen/Decoders/decoder.rb b/sim_gen/Decoders/decoder.rb index ee429bf..40f31ab 100644 --- a/sim_gen/Decoders/decoder.rb +++ b/sim_gen/Decoders/decoder.rb @@ -108,9 +108,15 @@ def filter_instructions(instructions, node, separ_mask) for insn in instructions insn_mask = calc_insn_mask(insn) insn_value = calc_insn_value(insn) - next if (insn_mask & separ_mask) != separ_mask - res << insn if (insn_value & separ_mask) == (node & separ_mask) + # PROPOSAL: + # Split ranges can contain bits that are fixed for one instruction + # format and non-fixed for others. + # e.g. in R4 FP instructions 31:27 are frs3, while FP R-format uses + # 31-24 as a fixed funct7. + # The solution is + relevant_mask = insn_mask & separ_mask + res << insn if (insn_value & relevant_mask) == (node & relevant_mask) end res end @@ -290,7 +296,11 @@ def generate_decoder(input_ir) namespace prot::decoder { using namespace isa; -std::optional decode(const isa::Word raw_insn) { +// PROPOSAL: +// Temporary: the current RV64 work still reuses RV32 instruction width, +// because isa::Word type is deducted from biggest reg size from regfile, which +// is 64 bit (FP regs). That conflicts with 32I that is already implemented. +std::optional decode(const uint32_t raw_insn) { Instruction insn{}; #{decoder_impl} return {}; // No matching instruction found diff --git a/sim_gen/ISA/isa.rb b/sim_gen/ISA/isa.rb index bed0076..3b12771 100644 --- a/sim_gen/ISA/isa.rb +++ b/sim_gen/ISA/isa.rb @@ -1,105 +1,106 @@ module SimGen - module ISA - module Helper - module_function - def find_max_reg(regfiles) - max_reg_size = 0 - regfiles.each do |regfile| - regfile[:regs].each do |reg| - if reg[:size] > max_reg_size - max_reg_size = reg[:size] - end - end - end - max_reg_size - end - - def find_max_operands(instructions) - max_operands = 0 - max_size = 0 - instructions.each do |insn| - operands_count = 0 - insn[:map][:tree].each do |node| - if node[:name] == :new_var && !node[:attrs].nil? && node[:attrs].include?(:op) - operands_count += 1 - max_size = Utility.get_type(node[:oprnds][0][:type]).bitsize if Utility.get_type(node[:oprnds][0][:type]).bitsize > max_size - end - end - max_operands = operands_count if operands_count > max_operands - end - [max_operands, max_size] - end - - def generate_fields_struct(max_operands, max_size) - emitter = Utility::GenEmitter.new - for i in 0...max_operands - emitter.emit_line("uint#{max_size}_t operand#{i};") - end - emitter.increase_indent_all(2) - emitter + module ISA + module Helper + module_function + + def find_max_reg(regfiles) + max_reg_size = 0 + regfiles.each do |regfile| + regfile[:regs].each do |reg| + max_reg_size = reg[:size] if reg[:size] > max_reg_size + end + end + max_reg_size + end + + def find_max_operands(instructions) + max_operands = 0 + max_size = 0 + instructions.each do |insn| + operands_count = 0 + insn[:map][:tree].each do |node| + next unless node[:name] == :new_var && !node[:attrs].nil? && node[:attrs].include?(:op) + + operands_count += 1 + if Utility.get_type(node[:oprnds][0][:type]).bitsize > max_size + max_size = Utility.get_type(node[:oprnds][0][:type]).bitsize end + end + max_operands = operands_count if operands_count > max_operands + end + [max_operands, max_size] + end - def generate_instruction_struct(input_ir) - max_operands, max_size = find_max_operands(input_ir[:instructions]) - fields_struct = generate_fields_struct(max_operands, max_size) -"struct Instruction { + def generate_fields_struct(max_operands, max_size) + emitter = Utility::GenEmitter.new + for i in 0...max_operands + emitter.emit_line("uint#{max_size}_t operand#{i};") + end + emitter.increase_indent_all(2) + emitter + end + + def generate_instruction_struct(input_ir) + max_operands, max_size = find_max_operands(input_ir[:instructions]) + fields_struct = generate_fields_struct(max_operands, max_size) + "struct Instruction { Opcode m_opc; -#{fields_struct.to_s} +#{fields_struct} };" - end + end - def get_addr_type(instructions) - instructions.each { |insn| - insn[:code][:tree].each { |node| - return node[:oprnds][0][:type] if node[:name] == :writeMem || node[:name] == :readMem - } - } - end - - def is_terminator_instruction(insn) - insn[:code][:tree].each { |node| - return true if node[:name] == :branch - } - false - end + def get_addr_type(instructions) + instructions.each do |insn| + insn[:code][:tree].each do |node| + return node[:oprnds][0][:type] if %i[writeMem readMem].include?(node[:name]) + end + end + end - def generate_is_terminator_function(input_ir) - emitter = Utility::GenEmitter.new - emitter.emit_line("inline constexpr bool isTerminator(Opcode opc) {") - emitter.increase_indent - emitter.emit_line("switch (opc) {") - emitter.increase_indent - input_ir[:instructions].each do |insn| - emitter.emit_line("case Opcode::k#{insn[:name].to_s.upcase}:") if is_terminator_instruction(insn) - end - emitter.emit_line("return true;") - emitter.decrease_indent - emitter.emit_line("default:") - emitter.increase_indent - emitter.emit_line("return false;") - emitter.decrease_indent - emitter.emit_line("}") - emitter.decrease_indent - emitter.emit_line("}") - emitter - end + def is_terminator_instruction(insn) + insn[:code][:tree].each do |node| + return true if node[:name] == :branch + end + false + end + + def generate_is_terminator_function(input_ir) + emitter = Utility::GenEmitter.new + emitter.emit_line('inline constexpr bool isTerminator(Opcode opc) {') + emitter.increase_indent + emitter.emit_line('switch (opc) {') + emitter.increase_indent + input_ir[:instructions].each do |insn| + emitter.emit_line("case Opcode::k#{insn[:name].to_s.upcase}:") if is_terminator_instruction(insn) end + emitter.emit_line('return true;') + emitter.decrease_indent + emitter.emit_line('default:') + emitter.increase_indent + emitter.emit_line('return false;') + emitter.decrease_indent + emitter.emit_line('}') + emitter.decrease_indent + emitter.emit_line('}') + emitter + end end + end end module SimGen - module ISA - module Header - module_function - - def generate_isa_header(input_ir) - type = Helper.get_addr_type input_ir[:instructions] - type_str = Utility::HelperCpp::gen_type type - - instruction_struct = Helper.generate_instruction_struct(input_ir) - is_terminator_function = Helper.generate_is_terminator_function(input_ir) - max_xlen = SimGen::Helper::find_max_xlen(input_ir[:regfiles]) -"#ifndef GENERATED_#{input_ir[:isa_name].upcase}_ISA_HH_INCLUDED + module ISA + module Header + module_function + + def generate_isa_header(input_ir) + type = Helper.get_addr_type input_ir[:instructions] + type_str = Utility::HelperCpp.gen_type type + + instruction_struct = Helper.generate_instruction_struct(input_ir) + is_terminator_function = Helper.generate_is_terminator_function(input_ir) + max_xlen = SimGen::Helper.find_max_xlen(input_ir[:regfiles]) + "#ifndef GENERATED_#{input_ir[:isa_name].upcase}_ISA_HH_INCLUDED #define GENERATED_#{input_ir[:isa_name].upcase}_ISA_HH_INCLUDED #include @@ -112,9 +113,9 @@ def generate_isa_header(input_ir) #{input_ir[:instructions].map { |insn| " k#{insn[:name].to_s.upcase}," }.join("\n")} }; -#{instruction_struct.to_s} +#{instruction_struct} -#{is_terminator_function.to_s} +#{is_terminator_function} inline constexpr std::size_t getILen(Opcode opc) { switch (opc) { @@ -126,7 +127,7 @@ def generate_isa_header(input_ir) } // namespace prot::isa #endif // GENERATED_#{input_ir[:isa_name].upcase}_ISA_HH_INCLUDED" - end - end + end end + end end diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..b0fa22f --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +project(protea_tests) + +include(GoogleTest) + +add_executable(rv64f_tests + rv64f/rv64f_tests.cc + ${protea_simgen_BINARY_DIR}/decoder.cc + ${protea_simgen_BINARY_DIR}/naive_interpreter.cc + ${protea_simgen_BINARY_DIR}/base_exec_engine.cc + ${protea_simlib_SOURCE_DIR}/memory.cc +) + +add_dependencies(rv64f_tests protea_simgen) + +target_include_directories(rv64f_tests + PRIVATE + ${protea_simgen_BINARY_DIR} + ${protea_simlib_SOURCE_DIR} +) + +target_link_libraries(rv64f_tests + PRIVATE + GTest::gtest_main + softfloat +) + +target_compile_features(rv64f_tests PRIVATE cxx_std_23) + +gtest_discover_tests(rv64f_tests) diff --git a/tests/rv64f/rv64f_tests.cc b/tests/rv64f/rv64f_tests.cc new file mode 100644 index 0000000..8602b8b --- /dev/null +++ b/tests/rv64f/rv64f_tests.cc @@ -0,0 +1,410 @@ +// PROPOSAL: +// Add unit tests for rv64f. + +#include "decoder.hh" +#include "memory.hh" +#include "naive_interpreter.hh" + +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include "softfloat.h" +} + +namespace { + +using prot::decoder::decode; +using prot::engine::Interpreter; +using prot::isa::Instruction; +using prot::isa::Opcode; +using prot::memory::makePlain; +using prot::state::CPU; + +} // namespace + +namespace prot::isa { +void PrintTo(Opcode opcode, std::ostream *os) { + *os << static_cast(opcode); +} +} // namespace prot::isa + +namespace { + +constexpr uint32_t kOpFp = 0b1010011; + +uint32_t encodeR(uint32_t funct7, uint32_t rs2, uint32_t rs1, uint32_t funct3, + uint32_t rd, uint32_t opcode = kOpFp) { + return (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | + (rd << 7) | opcode; +} + +uint32_t encodeR4(uint32_t funct2, uint32_t rs3, uint32_t rs2, uint32_t rs1, + uint32_t rm, uint32_t rd, uint32_t opcode) { + return (rs3 << 27) | (funct2 << 25) | (rs2 << 20) | (rs1 << 15) | (rm << 12) | + (rd << 7) | opcode; +} + +uint32_t encodeI(uint32_t imm, uint32_t rs1, uint32_t funct3, uint32_t rd, + uint32_t opcode) { + return ((imm & 0xfff) << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | + opcode; +} + +uint32_t encodeS(uint32_t imm, uint32_t rs2, uint32_t rs1, uint32_t funct3, + uint32_t opcode) { + return (((imm >> 5) & 0x7f) << 25) | (rs2 << 20) | (rs1 << 15) | + (funct3 << 12) | ((imm & 0x1f) << 7) | opcode; +} + +uint64_t boxF32(uint32_t value) { return 0xffffffff00000000ULL | value; } + +uint32_t low32(uint64_t value) { return static_cast(value); } + +Instruction decodeOne(uint32_t raw) { + auto insn = decode(raw); + EXPECT_TRUE(insn.has_value()) << "raw instruction: 0x" << std::hex << raw; + return *insn; +} + +void expectDecode(uint32_t raw, Opcode opcode) { + const auto insn = decodeOne(raw); + EXPECT_EQ(insn.m_opc, opcode); +} + +void expectDecodeOperands(uint32_t raw, Opcode opcode, + std::span operands) { + const auto insn = decodeOne(raw); + EXPECT_EQ(insn.m_opc, opcode); + const std::array actual{insn.operand0, insn.operand1, insn.operand2, + insn.operand3}; + for (std::size_t i = 0; i < operands.size(); ++i) { + EXPECT_EQ(actual[i], operands[i]) << "operand" << i; + } +} + +Instruction decodeAndExecute(CPU &cpu, uint32_t raw) { + auto insn = decodeOne(raw); + Interpreter interpreter; + interpreter.execute(cpu, insn); + return insn; +} + +void withSoftfloatRounding(uint_fast8_t rm, const std::function &body) { + const auto saved = softfloat_roundingMode; + softfloat_roundingMode = rm; + body(); + softfloat_roundingMode = saved; +} + +struct RoundingOpcodes { + std::array opcodes; +}; + +constexpr std::array kRoundingModes{0, 1, 2, 3, 4}; + +void expectRoundedRFamily(uint32_t funct7, const RoundingOpcodes &ops) { + for (std::size_t i = 0; i < kRoundingModes.size(); ++i) { + expectDecode(encodeR(funct7, 3, 2, kRoundingModes[i], 1), ops.opcodes[i]); + } +} + +void expectRoundedR4Family(uint32_t opcode, uint32_t funct2, + const RoundingOpcodes &ops) { + for (std::size_t i = 0; i < kRoundingModes.size(); ++i) { + expectDecode(encodeR4(funct2, 4, 3, 2, kRoundingModes[i], 1, opcode), + ops.opcodes[i]); + } +} + +void expectRoundedFcvtToXFamily(uint32_t funct7, uint32_t rs2, + const RoundingOpcodes &ops) { + for (std::size_t i = 0; i < kRoundingModes.size(); ++i) { + expectDecode(encodeR(funct7, rs2, 2, kRoundingModes[i], 1), ops.opcodes[i]); + } +} + +void expectRoundedFcvtToFFamily(uint32_t funct7, uint32_t rs2, + const RoundingOpcodes &ops) { + for (std::size_t i = 0; i < kRoundingModes.size(); ++i) { + expectDecode(encodeR(funct7, rs2, 2, kRoundingModes[i], 1), ops.opcodes[i]); + } +} + +TEST(RV64FDecodeTest, DecodesAllRoundedArithmeticOpcodes) { + expectRoundedRFamily(0b0000000, {{{Opcode::kFADD_S_RNE, Opcode::kFADD_S_RTZ, + Opcode::kFADD_S_RDN, Opcode::kFADD_S_RUP, + Opcode::kFADD_S_RMM}}}); + expectRoundedRFamily(0b0000001, {{{Opcode::kFADD_D_RNE, Opcode::kFADD_D_RTZ, + Opcode::kFADD_D_RDN, Opcode::kFADD_D_RUP, + Opcode::kFADD_D_RMM}}}); + expectRoundedRFamily(0b0000100, {{{Opcode::kFSUB_S_RNE, Opcode::kFSUB_S_RTZ, + Opcode::kFSUB_S_RDN, Opcode::kFSUB_S_RUP, + Opcode::kFSUB_S_RMM}}}); + expectRoundedRFamily(0b0000101, {{{Opcode::kFSUB_D_RNE, Opcode::kFSUB_D_RTZ, + Opcode::kFSUB_D_RDN, Opcode::kFSUB_D_RUP, + Opcode::kFSUB_D_RMM}}}); + expectRoundedRFamily(0b0001000, {{{Opcode::kFMUL_S_RNE, Opcode::kFMUL_S_RTZ, + Opcode::kFMUL_S_RDN, Opcode::kFMUL_S_RUP, + Opcode::kFMUL_S_RMM}}}); + expectRoundedRFamily(0b0001001, {{{Opcode::kFMUL_D_RNE, Opcode::kFMUL_D_RTZ, + Opcode::kFMUL_D_RDN, Opcode::kFMUL_D_RUP, + Opcode::kFMUL_D_RMM}}}); + expectRoundedRFamily(0b0001100, {{{Opcode::kFDIV_S_RNE, Opcode::kFDIV_S_RTZ, + Opcode::kFDIV_S_RDN, Opcode::kFDIV_S_RUP, + Opcode::kFDIV_S_RMM}}}); + expectRoundedRFamily(0b0001101, {{{Opcode::kFDIV_D_RNE, Opcode::kFDIV_D_RTZ, + Opcode::kFDIV_D_RDN, Opcode::kFDIV_D_RUP, + Opcode::kFDIV_D_RMM}}}); + expectRoundedRFamily(0b0101100, {{{Opcode::kFSQRT_S_RNE, Opcode::kFSQRT_S_RTZ, + Opcode::kFSQRT_S_RDN, Opcode::kFSQRT_S_RUP, + Opcode::kFSQRT_S_RMM}}}); + expectRoundedRFamily(0b0101101, {{{Opcode::kFSQRT_D_RNE, Opcode::kFSQRT_D_RTZ, + Opcode::kFSQRT_D_RDN, Opcode::kFSQRT_D_RUP, + Opcode::kFSQRT_D_RMM}}}); +} + +TEST(RV64FDecodeTest, DecodesAllFusedMultiplyAddOpcodes) { + expectRoundedR4Family( + 0b1000011, 0b00, + {{{Opcode::kFMADD_S_RNE, Opcode::kFMADD_S_RTZ, Opcode::kFMADD_S_RDN, + Opcode::kFMADD_S_RUP, Opcode::kFMADD_S_RMM}}}); + expectRoundedR4Family( + 0b1000011, 0b01, + {{{Opcode::kFMADD_D_RNE, Opcode::kFMADD_D_RTZ, Opcode::kFMADD_D_RDN, + Opcode::kFMADD_D_RUP, Opcode::kFMADD_D_RMM}}}); + expectRoundedR4Family( + 0b1000111, 0b00, + {{{Opcode::kFMSUB_S_RNE, Opcode::kFMSUB_S_RTZ, Opcode::kFMSUB_S_RDN, + Opcode::kFMSUB_S_RUP, Opcode::kFMSUB_S_RMM}}}); + expectRoundedR4Family( + 0b1000111, 0b01, + {{{Opcode::kFMSUB_D_RNE, Opcode::kFMSUB_D_RTZ, Opcode::kFMSUB_D_RDN, + Opcode::kFMSUB_D_RUP, Opcode::kFMSUB_D_RMM}}}); + expectRoundedR4Family( + 0b1001111, 0b00, + {{{Opcode::kFNMADD_S_RNE, Opcode::kFNMADD_S_RTZ, Opcode::kFNMADD_S_RDN, + Opcode::kFNMADD_S_RUP, Opcode::kFNMADD_S_RMM}}}); + expectRoundedR4Family( + 0b1001111, 0b01, + {{{Opcode::kFNMADD_D_RNE, Opcode::kFNMADD_D_RTZ, Opcode::kFNMADD_D_RDN, + Opcode::kFNMADD_D_RUP, Opcode::kFNMADD_D_RMM}}}); + expectRoundedR4Family( + 0b1001011, 0b00, + {{{Opcode::kFNMSUB_S_RNE, Opcode::kFNMSUB_S_RTZ, Opcode::kFNMSUB_S_RDN, + Opcode::kFNMSUB_S_RUP, Opcode::kFNMSUB_S_RMM}}}); + expectRoundedR4Family( + 0b1001011, 0b01, + {{{Opcode::kFNMSUB_D_RNE, Opcode::kFNMSUB_D_RTZ, Opcode::kFNMSUB_D_RDN, + Opcode::kFNMSUB_D_RUP, Opcode::kFNMSUB_D_RMM}}}); +} + +TEST(RV64FDecodeTest, DecodesMemoryAndNonRoundedOpcodes) { + expectDecode(encodeI(0x10, 2, 0b010, 1, 0b0000111), Opcode::kFLW); + expectDecode(encodeI(0x10, 2, 0b011, 1, 0b0000111), Opcode::kFLD); + expectDecode(encodeS(0x10, 3, 2, 0b010, 0b0100111), Opcode::kFSW); + expectDecode(encodeS(0x10, 3, 2, 0b011, 0b0100111), Opcode::kFSD); + + expectDecode(encodeR(0b0010000, 3, 2, 0b000, 1), Opcode::kFSGNJ_S); + expectDecode(encodeR(0b0010001, 3, 2, 0b000, 1), Opcode::kFSGNJ_D); + expectDecode(encodeR(0b0010000, 3, 2, 0b001, 1), Opcode::kFSGNJN_S); + expectDecode(encodeR(0b0010001, 3, 2, 0b001, 1), Opcode::kFSGNJN_D); + expectDecode(encodeR(0b0010000, 3, 2, 0b010, 1), Opcode::kFSGNJX_S); + expectDecode(encodeR(0b0010001, 3, 2, 0b010, 1), Opcode::kFSGNJX_D); + + expectDecode(encodeR(0b0010100, 3, 2, 0b000, 1), Opcode::kFMIN_S); + expectDecode(encodeR(0b0010101, 3, 2, 0b000, 1), Opcode::kFMIN_D); + expectDecode(encodeR(0b0010100, 3, 2, 0b001, 1), Opcode::kFMAX_S); + expectDecode(encodeR(0b0010101, 3, 2, 0b001, 1), Opcode::kFMAX_D); + + expectDecode(encodeR(0b1010000, 3, 2, 0b010, 1), Opcode::kFEQ_S); + expectDecode(encodeR(0b1010001, 3, 2, 0b010, 1), Opcode::kFEQ_D); + expectDecode(encodeR(0b1010000, 3, 2, 0b001, 1), Opcode::kFLT_S); + expectDecode(encodeR(0b1010001, 3, 2, 0b001, 1), Opcode::kFLT_D); + expectDecode(encodeR(0b1010000, 3, 2, 0b000, 1), Opcode::kFLE_S); + expectDecode(encodeR(0b1010001, 3, 2, 0b000, 1), Opcode::kFLE_D); + + expectDecode(encodeR(0b1110000, 0, 2, 0b001, 1), Opcode::kFCLASS_S); + expectDecode(encodeR(0b1110001, 0, 2, 0b001, 1), Opcode::kFCLASS_D); + expectDecode(encodeR(0b1110000, 0, 2, 0b000, 1), Opcode::kFMV_X_W); + expectDecode(encodeR(0b1111000, 0, 2, 0b000, 1), Opcode::kFMV_W_X); +} + +TEST(RV64FDecodeTest, DecodesAllConversionOpcodes) { + expectRoundedFcvtToXFamily( + 0b1100000, 0b00000, + {{{Opcode::kFCVT_W_S_RNE, Opcode::kFCVT_W_S_RTZ, Opcode::kFCVT_W_S_RDN, + Opcode::kFCVT_W_S_RUP, Opcode::kFCVT_W_S_RMM}}}); + expectRoundedFcvtToXFamily( + 0b1100000, 0b00001, + {{{Opcode::kFCVT_WU_S_RNE, Opcode::kFCVT_WU_S_RTZ, Opcode::kFCVT_WU_S_RDN, + Opcode::kFCVT_WU_S_RUP, Opcode::kFCVT_WU_S_RMM}}}); + expectRoundedFcvtToXFamily( + 0b1100000, 0b00010, + {{{Opcode::kFCVT_L_S_RNE, Opcode::kFCVT_L_S_RTZ, Opcode::kFCVT_L_S_RDN, + Opcode::kFCVT_L_S_RUP, Opcode::kFCVT_L_S_RMM}}}); + expectRoundedFcvtToXFamily( + 0b1100000, 0b00011, + {{{Opcode::kFCVT_LU_S_RNE, Opcode::kFCVT_LU_S_RTZ, Opcode::kFCVT_LU_S_RDN, + Opcode::kFCVT_LU_S_RUP, Opcode::kFCVT_LU_S_RMM}}}); + expectRoundedFcvtToFFamily( + 0b1101000, 0b00000, + {{{Opcode::kFCVT_S_W_RNE, Opcode::kFCVT_S_W_RTZ, Opcode::kFCVT_S_W_RDN, + Opcode::kFCVT_S_W_RUP, Opcode::kFCVT_S_W_RMM}}}); + expectRoundedFcvtToFFamily( + 0b1101000, 0b00001, + {{{Opcode::kFCVT_S_WU_RNE, Opcode::kFCVT_S_WU_RTZ, Opcode::kFCVT_S_WU_RDN, + Opcode::kFCVT_S_WU_RUP, Opcode::kFCVT_S_WU_RMM}}}); + expectRoundedFcvtToFFamily( + 0b1101000, 0b00010, + {{{Opcode::kFCVT_S_L_RNE, Opcode::kFCVT_S_L_RTZ, Opcode::kFCVT_S_L_RDN, + Opcode::kFCVT_S_L_RUP, Opcode::kFCVT_S_L_RMM}}}); + expectRoundedFcvtToFFamily( + 0b1101000, 0b00011, + {{{Opcode::kFCVT_S_LU_RNE, Opcode::kFCVT_S_LU_RTZ, Opcode::kFCVT_S_LU_RDN, + Opcode::kFCVT_S_LU_RUP, Opcode::kFCVT_S_LU_RMM}}}); +} + +TEST(RV64FDecodeTest, DecodesExpectedOperandOrderForRepresentativeFormats) { + expectDecodeOperands(encodeR(0b0000000, 3, 2, 0, 1), Opcode::kFADD_S_RNE, + std::array{3, 2, 1}); + expectDecodeOperands(encodeR4(0, 4, 3, 2, 0, 1, 0b1000011), + Opcode::kFMADD_S_RNE, + std::array{4, 3, 2, 1}); + expectDecodeOperands(encodeI(0x10, 2, 0b010, 1, 0b0000111), Opcode::kFLW, + std::array{0x10, 2, 1}); + expectDecodeOperands(encodeS(0x10, 3, 2, 0b010, 0b0100111), Opcode::kFSW, + std::array{0x10, 2, 3}); +} + +TEST(RV64FExecutionTest, ExecutesSingleAndDoubleArithmetic) { + auto memory = makePlain(256); + CPU cpu(memory.get()); + cpu.setFRegs(2, boxF32(0x3fc00000)); // 1.5f + cpu.setFRegs(3, boxF32(0x40100000)); // 2.25f + + withSoftfloatRounding(softfloat_round_near_even, [&] { + const float32_t lhs{0x3fc00000}; + const float32_t rhs{0x40100000}; + decodeAndExecute(cpu, encodeR(0b0000000, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(f32_add(lhs, rhs).v)); + decodeAndExecute(cpu, encodeR(0b0000100, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(f32_sub(lhs, rhs).v)); + decodeAndExecute(cpu, encodeR(0b0001000, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(f32_mul(lhs, rhs).v)); + decodeAndExecute(cpu, encodeR(0b0001100, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(f32_div(lhs, rhs).v)); + decodeAndExecute(cpu, encodeR(0b0101100, 0, 3, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(f32_sqrt(rhs).v)); + }); + + cpu.setFRegs(2, 0x3ff8000000000000ULL); // 1.5 + cpu.setFRegs(3, 0x4002000000000000ULL); // 2.25 + + withSoftfloatRounding(softfloat_round_near_even, [&] { + const float64_t lhs{0x3ff8000000000000ULL}; + const float64_t rhs{0x4002000000000000ULL}; + decodeAndExecute(cpu, encodeR(0b0000001, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), f64_add(lhs, rhs).v); + decodeAndExecute(cpu, encodeR(0b0000101, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), f64_sub(lhs, rhs).v); + decodeAndExecute(cpu, encodeR(0b0001001, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), f64_mul(lhs, rhs).v); + decodeAndExecute(cpu, encodeR(0b0001101, 3, 2, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), f64_div(lhs, rhs).v); + decodeAndExecute(cpu, encodeR(0b0101101, 0, 3, 0, 1)); + EXPECT_EQ(cpu.getFRegs(1), f64_sqrt(rhs).v); + }); +} + +TEST(RV64FExecutionTest, ExecutesMemorySignCompareClassAndMoveInstructions) { + auto memory = makePlain(256); + CPU cpu(memory.get()); + cpu.setXRegs(2, 64U); + memory->write(68, 0x3f800000U); + memory->write(80, 0x4000000000000000ULL); + + decodeAndExecute(cpu, encodeI(4, 2, 0b010, 1, 0b0000111)); + EXPECT_EQ(low32(cpu.getFRegs(1)), 0x3f800000U); + + decodeAndExecute(cpu, encodeI(16, 2, 0b011, 1, 0b0000111)); + EXPECT_EQ(cpu.getFRegs(1), 0x4000000000000000ULL); + + cpu.setFRegs(3, boxF32(0x40400000)); + decodeAndExecute(cpu, encodeS(24, 3, 2, 0b010, 0b0100111)); + EXPECT_EQ(memory->read(88), 0x40400000U); + + cpu.setFRegs(3, 0x4008000000000000ULL); + decodeAndExecute(cpu, encodeS(32, 3, 2, 0b011, 0b0100111)); + EXPECT_EQ(memory->read(96), 0x4008000000000000ULL); + + cpu.setFRegs(2, boxF32(0x3f800000)); + cpu.setFRegs(3, boxF32(0x80000000)); + decodeAndExecute(cpu, encodeR(0b0010000, 3, 2, 0b000, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(0xbf800000)); + decodeAndExecute(cpu, encodeR(0b0010000, 3, 2, 0b001, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(0x3f800000)); + decodeAndExecute(cpu, encodeR(0b0010000, 3, 2, 0b010, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(0xbf800000)); + + cpu.setFRegs(2, boxF32(0x3f800000)); + cpu.setFRegs(3, boxF32(0x40000000)); + decodeAndExecute(cpu, encodeR(0b0010100, 3, 2, 0b000, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(0x3f800000)); + decodeAndExecute(cpu, encodeR(0b0010100, 3, 2, 0b001, 1)); + EXPECT_EQ(cpu.getFRegs(1), boxF32(0x40000000)); + decodeAndExecute(cpu, encodeR(0b1010000, 3, 2, 0b010, 1)); + EXPECT_EQ(cpu.getXRegs(1), 0U); + decodeAndExecute(cpu, encodeR(0b1010000, 3, 2, 0b001, 1)); + EXPECT_EQ(cpu.getXRegs(1), 1U); + decodeAndExecute(cpu, encodeR(0b1010000, 3, 2, 0b000, 1)); + EXPECT_EQ(cpu.getXRegs(1), 1U); + + decodeAndExecute(cpu, encodeR(0b1110000, 0, 2, 0b001, 1)); + EXPECT_EQ(cpu.getXRegs(1), 1U << 6); + cpu.setFRegs(2, 0x7ff8000000000000ULL); + decodeAndExecute(cpu, encodeR(0b1110001, 0, 2, 0b001, 1)); + EXPECT_EQ(cpu.getXRegs(1), 1U << 9); + + cpu.setFRegs(2, boxF32(0xdeadbeef)); + decodeAndExecute(cpu, encodeR(0b1110000, 0, 2, 0b000, 1)); + EXPECT_EQ(cpu.getXRegs(1), 0xdeadbeefU); + + cpu.setXRegs(2, 0x3f800000U); + decodeAndExecute(cpu, encodeR(0b1111000, 0, 2, 0b000, 1)); + EXPECT_EQ(low32(cpu.getFRegs(1)), 0x3f800000U); +} + +TEST(RV64FExecutionTest, ExecutesConversionsRepresentableByCurrentXRegWidth) { + auto memory = makePlain(256); + CPU cpu(memory.get()); + + cpu.setFRegs(2, boxF32(0x40200000)); // 2.5f + decodeAndExecute(cpu, encodeR(0b1100000, 0b00000, 2, 0b001, 1)); + EXPECT_EQ(cpu.getXRegs(1), 2U); + decodeAndExecute(cpu, encodeR(0b1100000, 0b00001, 2, 0b001, 1)); + EXPECT_EQ(cpu.getXRegs(1), 2U); + decodeAndExecute(cpu, encodeR(0b1100000, 0b00010, 2, 0b001, 1)); + EXPECT_EQ(cpu.getXRegs(1), 2U); + decodeAndExecute(cpu, encodeR(0b1100000, 0b00011, 2, 0b001, 1)); + EXPECT_EQ(cpu.getXRegs(1), 2U); + + cpu.setXRegs(2, 3U); + decodeAndExecute(cpu, encodeR(0b1101000, 0b00000, 2, 0b000, 1)); + EXPECT_EQ(low32(cpu.getFRegs(1)), 0x40400000U); + decodeAndExecute(cpu, encodeR(0b1101000, 0b00001, 2, 0b000, 1)); + EXPECT_EQ(low32(cpu.getFRegs(1)), 0x40400000U); + decodeAndExecute(cpu, encodeR(0b1101000, 0b00010, 2, 0b000, 1)); + EXPECT_EQ(low32(cpu.getFRegs(1)), 0x40400000U); + decodeAndExecute(cpu, encodeR(0b1101000, 0b00011, 2, 0b000, 1)); + EXPECT_EQ(low32(cpu.getFRegs(1)), 0x40400000U); +} + +} // namespace From 21295f5fdb765185e513fc3d5b24aacc42723ef2 Mon Sep 17 00:00:00 2001 From: doushe821 Date: Tue, 28 Apr 2026 21:35:03 +0300 Subject: [PATCH 8/8] [MR] Added more proposal comments --- CMakeLists.txt | 2 +- cmake/dependencies.cmake | 2 ++ code_gen/cpp_gen.rb | 30 +++++++++++++++++++++++++----- lib/ADL/builder.rb | 3 +++ lib/ADL/scope.rb | 7 +++++-- lib/ADL/value.rb | 2 ++ lib/ADL/var.rb | 1 + lib/Target/RISC-V/32I.rb | 1 + lib/Target/RISC-V/32ILoops.rb | 1 + lib/Target/RISC-V/64F.rb | 4 ++-- lib/Target/RISC-V/encoding.rb | 3 +++ lib/Utility/fp_helper_cpp.rb | 4 ++++ lib/Utility/type.rb | 1 + ser2ruby/base2ruby.rb | 1 + sim_gen/CPUState/cpu_state.rb | 1 + sim_gen/Decoders/decoder.rb | 3 ++- sim_gen/ISA/isa.rb | 1 + 17 files changed, 56 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04a60ae..a73ea2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,6 @@ add_subdirectory(lib) add_subdirectory(sim_lib) add_subdirectory(sim_gen) -if(BUILD_TESTING) +if(UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index e133004..af16461 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -22,6 +22,8 @@ CPMAddPackage( EXCLUDE_FROM_ALL True SYSTEM True) +# PROPOSAL: +# Add GTest for unit testing. if(UNIT_TESTS) # GoogleTest: C++ unit testing framework CPMAddPackage( diff --git a/code_gen/cpp_gen.rb b/code_gen/cpp_gen.rb index 0f92cf5..812a192 100644 --- a/code_gen/cpp_gen.rb +++ b/code_gen/cpp_gen.rb @@ -7,6 +7,10 @@ module CodeGen class CppGenerator attr_reader :emitter, :mapping + # PROPOSAL: + # Add RM (rounding modes) map that converts + # RV rounding modes into softfloat rounding modes. + # They actually are exactly the same, but we can generalize this idea later. # Maybe should move it to other module # RISC-V RM to SoftFloat RM mapping # RNE=0 -> softfloat_round_near_even(0), RTZ=1 -> softfloat_round_minMag(1), @@ -37,10 +41,16 @@ def binary_operation(emitter, operation, op_str) emitter.emit_line("#{dst} = #{src1} #{op_str} #{src2};") end + # PROPOSAL: + # Add FP emitter helpers # Emit code to set SoftFloat rounding mode before FP operation + # For most instruction in softfloat, rounding mode is defined + # by global variable, so we have to change it before we execute. + # We also store it into a temporal variable, so we can restore it later. def emit_rm_setup(rm, tmp_var = '_rm_save') return unless rm # nil means no rm handling needed + # Currently not supported if rm == 7 # DYN mode - read from fcsr at runtime @emitter.emit_line('assert(0 && "CSR is currently not supported\n");') @emitter.emit_line("uint_fast8_t #{tmp_var} = softfloat_roundingMode;") @@ -57,6 +67,10 @@ def emit_rm_restore(tmp_var = '_rm_save') @emitter.emit_line("softfloat_roundingMode = #{tmp_var};") end + # Emits binary fp operation. + # This is different from RV32I binop, because: + # 1. typing is different (check Utility/fp_helper_cpp.rb) + # 2. Rounding mode has to be changed def emit_fp_binary(opname, operation, dst_type:, src_types:, rm: nil) dst = map_operand(operation[:oprnds][0]) src1 = map_operand(operation[:oprnds][1]) @@ -100,7 +114,7 @@ def emit_fp_binary(opname, operation, dst_type:, src_types:, rm: nil) emit_rm_restore if rm end - # -> int conversions need to be handled separetely, because + # float -> int conversions need to be handled separetely, because # they break general pattern of softfloat instructions by demanding # rounding mode as an argument (they ignore global constant that all other # fp functions use for some unknown reason). @@ -179,6 +193,7 @@ def gen_fp_neg(tmp_name, type) end end + # same as above def emit_fp_unary(opname, operation, dst_type:, src_type:, rm: nil) dst = map_operand(operation[:oprnds][0]) src = map_operand(operation[:oprnds][1]) @@ -212,6 +227,8 @@ def emit_fp_unary(opname, operation, dst_type:, src_type:, rm: nil) emit_rm_restore if rm end + # RV64F has FMA instructions that have 3 sources, so this + # new emitter is necessary. def emit_fp_ternary(opname, operation, dst_type:, src_types:, @@ -269,6 +286,7 @@ def emit_fp_ternary(opname, operation, emit_rm_restore if rm end + # No softfloat lib function, so hand-written again def emit_sign_inject(operation, width:, mode:) dst, src1, src2 = map_n_operands(operation, 3) @@ -295,6 +313,7 @@ def emit_sign_inject(operation, width:, mode:) @emitter.emit_line("#{dst} = #{result};") end + # Helpers for easier operand mapping in operations emitters def map_n_operands(op, n) ops = [] (0...n).each do |i| @@ -331,7 +350,7 @@ def cpu_read_mem(dst, addr) end def generate_statement(operation) - # Extract rm from attrs if present + # Extract rm from attrs if present. rm = operation[:attrs] if operation[:attrs].is_a?(Integer) case operation[:name] @@ -394,7 +413,6 @@ def generate_statement(operation) @emitter.emit_line("#{expr} = #{cpu_read_reg(src)}<#{Utility::HelperCpp.gen_small_type(src[:type])}>(#{src_name});") when :writeReg dst = operation[:oprnds][0] - src = operation[:oprnds][1] dst_name = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name] expr = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name] expr = expr.nil? ? operation[:oprnds][1][:value] : expr @@ -426,6 +444,8 @@ def generate_statement(operation) @emitter.emit_line("#{dst} = #{cond} ? #{true_val} : #{false_val};") + # PROPOSAL: + # Add emitters for RV64F # Floating point arithmetic operations WITH rounding mode (SoftFloat global) when :f32_add then emit_fp_binary('f32_add', operation, dst_type: :f32, @@ -522,7 +542,7 @@ def generate_statement(operation) negate_src: [0, 2], rm: rm) - # Floating point comparisons (NO rounding mode - SoftFloat doesn't need it) + # Floating point comparisons (no rm) when :f32_eq then emit_fp_binary('f32_eq', operation, dst_type: :i32, src_types: %i[f32 f32]) @@ -557,7 +577,7 @@ def generate_statement(operation) when :f32_sign_xor then emit_sign_inject(operation, width: 32, mode: :xor) when :f64_sign_xor then emit_sign_inject(operation, width: 64, mode: :xor) - # Floating point conversions WITH rounding mode (SoftFloat global) + # Floating point conversions with rm when :f32_to_i32 then emit_fp_to_int_conv('f32_to_i32', operation, dst_type: :i32, src_type: :f32, rounding_mode: rm) when :f32_to_u32 then emit_fp_to_int_conv('f32_to_ui32', operation, dst_type: :u32, src_type: :f32, diff --git a/lib/ADL/builder.rb b/lib/ADL/builder.rb index 75b5b66..3a81935 100644 --- a/lib/ADL/builder.rb +++ b/lib/ADL/builder.rb @@ -1,3 +1,6 @@ +# PROPOSAL: +# Autoformatter works on file save, so I had no choice) +# (autoformatted) require_relative 'scope' require 'Utility/type' diff --git a/lib/ADL/scope.rb b/lib/ADL/scope.rb index 6a1b110..80aed1e 100644 --- a/lib/ADL/scope.rb +++ b/lib/ADL/scope.rb @@ -59,7 +59,7 @@ def resolve_const(what) Constant.new(self, "const_#{next_counter}", what) if what.class == Integer end - # SUGGESTION: + # PROPOSAL: # Make those helpers accept optional attrs argument, # which is now used to pass rounding mode for some fp instructions. # Other pseudo-operands may appear in other RV modules, @@ -119,7 +119,8 @@ def or(a, b, attrs = nil) = binOp(a, b, :or, attrs) def and(a, b, attrs = nil) = binOp(a, b, :and, attrs) def eq(a, b, attrs = nil) = binOpWType(a, b, :eq, :b1, attrs) def ne(a, b, attrs = nil) = binOpWType(a, b, :ne, :b1, attrs) - + # PROPOSAL: + # Add floatig point operations # Floating point Arithmetic def f32_add(a, b, rm = nil) = binOp(a, b, :f32_add, rm) def f64_add(a, b, rm = nil) = binOp(a, b, :f64_add, rm) @@ -252,6 +253,8 @@ def stmt(name, operands, attrs = nil) def read_transform(operation_name, op) if op.class == Var && !op.regset.nil? case op.regset + # PROPOSAL: + # add switch case to support FRegs when :XRegs then x = tmpvar(('b' + op.type.to_s[1..-1]).to_sym) when :FRegs then x = tmpvar(('f' + op.type.to_s[1..-1]).to_sym) else raise 'Unknown regset' diff --git a/lib/ADL/value.rb b/lib/ADL/value.rb index cb26f7d..d25fb14 100644 --- a/lib/ADL/value.rb +++ b/lib/ADL/value.rb @@ -1,3 +1,5 @@ +# PROPOSAL: +# autoformat again module SimInfra # Value class is a super class of Variable or Constant. class Value diff --git a/lib/ADL/var.rb b/lib/ADL/var.rb index 58126f0..876b46d 100644 --- a/lib/ADL/var.rb +++ b/lib/ADL/var.rb @@ -55,6 +55,7 @@ def ==(other) = @scope.eq(self, other) def !=(other) = @scope.ne(self, other) def [](r, l) = @scope.extract(self, r, l) + # PROPOSAL: add f regs def f = @scope.cast(self, ('f' + Utility.get_type(@type).bitsize.to_s).to_sym) def u = @scope.cast(self, ('u' + Utility.get_type(@type).bitsize.to_s).to_sym) def s = @scope.cast(self, ('s' + Utility.get_type(@type).bitsize.to_s).to_sym) diff --git a/lib/Target/RISC-V/32I.rb b/lib/Target/RISC-V/32I.rb index bdf88fe..a1cc076 100644 --- a/lib/Target/RISC-V/32I.rb +++ b/lib/Target/RISC-V/32I.rb @@ -1,3 +1,4 @@ +# PROPOSAL: autoformat require_relative 'encoding' require_relative '../../ADL/base' require_relative '../../ADL/builder' diff --git a/lib/Target/RISC-V/32ILoops.rb b/lib/Target/RISC-V/32ILoops.rb index 5a144de..9f38175 100644 --- a/lib/Target/RISC-V/32ILoops.rb +++ b/lib/Target/RISC-V/32ILoops.rb @@ -1,3 +1,4 @@ +# PROPOSAL: autoformat require_relative 'encoding' require_relative '../../Generic/base' require_relative '../../Generic/builder' diff --git a/lib/Target/RISC-V/64F.rb b/lib/Target/RISC-V/64F.rb index a0bba6b..45884e0 100644 --- a/lib/Target/RISC-V/64F.rb +++ b/lib/Target/RISC-V/64F.rb @@ -1,3 +1,5 @@ +# PROPOSAL: +# Add rv64f isa description require_relative 'encoding' require_relative '../../ADL/base' require_relative '../../ADL/builder' @@ -6,8 +8,6 @@ module RV64F include SimInfra extend SimInfra - # NOTE: semantics now are just dummies, they will change in future - # Register float regs in regfile RegisterFile(:FRegs) do (0..31).each do |i| send(:f64, :"f#{i}") diff --git a/lib/Target/RISC-V/encoding.rb b/lib/Target/RISC-V/encoding.rb index e32f4b4..2b0dcbb 100644 --- a/lib/Target/RISC-V/encoding.rb +++ b/lib/Target/RISC-V/encoding.rb @@ -1,3 +1,4 @@ +# PROPOSAL: autoformat require_relative '../../ADL/base' module SimInfra @@ -56,6 +57,8 @@ def format_r(opcode, funct3, funct7) ], xreg(:rs2), xreg(:rs1), xreg(:rd)] end + # PROPOSAL: + # Add FP encoding formats. def format_r4_fp(opcode, funct2, rm) [:R4_FP, [ field(:f_opcode, 6, 0, opcode), diff --git a/lib/Utility/fp_helper_cpp.rb b/lib/Utility/fp_helper_cpp.rb index d1400c0..600d489 100644 --- a/lib/Utility/fp_helper_cpp.rb +++ b/lib/Utility/fp_helper_cpp.rb @@ -1,3 +1,7 @@ +# PROPOSAL: +# Add utility methods for FP operatiosn to support typing of softfloat. +# float32_t and float64_t are actually C structures that contain a +# uint32_t / uint64_t raw bytes memories. # Utility methods for FP instructions generation module Utility extend Utility diff --git a/lib/Utility/type.rb b/lib/Utility/type.rb index ebe4b38..4efe81e 100644 --- a/lib/Utility/type.rb +++ b/lib/Utility/type.rb @@ -1,3 +1,4 @@ +# PROPOSAL: autoformat module Utility # The instances of the Type class are immutable. # Only one instance of a particular type is ever created diff --git a/ser2ruby/base2ruby.rb b/ser2ruby/base2ruby.rb index 7ef3171..54cec6d 100644 --- a/ser2ruby/base2ruby.rb +++ b/ser2ruby/base2ruby.rb @@ -1,3 +1,4 @@ +# PROPOSAL: autoformat require 'Utility/gen_emitter' # Helper methods for Intermediate Representation diff --git a/sim_gen/CPUState/cpu_state.rb b/sim_gen/CPUState/cpu_state.rb index 8b29cd7..d1ce2d8 100644 --- a/sim_gen/CPUState/cpu_state.rb +++ b/sim_gen/CPUState/cpu_state.rb @@ -1,3 +1,4 @@ +# PROPOSAL: autoformat # frozen_string_literal: true require 'lib/Utility/gen_emitter' diff --git a/sim_gen/Decoders/decoder.rb b/sim_gen/Decoders/decoder.rb index 40f31ab..eb32869 100644 --- a/sim_gen/Decoders/decoder.rb +++ b/sim_gen/Decoders/decoder.rb @@ -114,7 +114,8 @@ def filter_instructions(instructions, node, separ_mask) # format and non-fixed for others. # e.g. in R4 FP instructions 31:27 are frs3, while FP R-format uses # 31-24 as a fixed funct7. - # The solution is + # The solution is to check relevant mask, instead of skipping + # instruction, if separ_mask != insn_mask: relevant_mask = insn_mask & separ_mask res << insn if (insn_value & relevant_mask) == (node & relevant_mask) end diff --git a/sim_gen/ISA/isa.rb b/sim_gen/ISA/isa.rb index 3b12771..4ac9920 100644 --- a/sim_gen/ISA/isa.rb +++ b/sim_gen/ISA/isa.rb @@ -1,3 +1,4 @@ +# PROPOSAL: autoformat module SimGen module ISA module Helper