Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +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(UNIT_TESTS)
add_subdirectory(tests)
endif()
15 changes: 15 additions & 0 deletions cmake/dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,18 @@ CPMAddPackage(
GIT_TAG 12.0.0
EXCLUDE_FROM_ALL True
SYSTEM True)

# Add GTest for unit testing.
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()
58 changes: 58 additions & 0 deletions cmake/softfloat.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# softfloat: floating point arithmetic library
CPMAddPackage(
NAME softfloat
GITHUB_REPOSITORY ucb-bar/berkeley-softfloat-3
GIT_TAG a0c6494cdc11865811dec815d5c0049fba9d82a8
DOWNLOAD_ONLY YES
EXCLUDE_FROM_ALL True
SYSTEM True)

# 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

#include <stdint.h>
#include <stdbool.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
)

# Use the RISC-V specialization so NaN handling, default-NaN values and
# float-to-int saturation match the RISC-V F/D spec (e.g. default NaN
# 0x7FC00000, i32_fromNaN 0x7FFFFFFF) rather than the 8086/x86 semantics.
file(GLOB SOFTFLOAT_PLATFORM_SRC
${softfloat_SOURCE_DIR}/source/RISCV/*.c

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider architecture specific.

)
set(SOFTFLOAT_PLATFORM_DIR ${softfloat_SOURCE_DIR}/source/RISCV)

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}/source
${SOFTFLOAT_PLATFORM_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)

target_compile_definitions(softfloat PRIVATE
SOFTFLOAT_FAST_INT64
SOFTFLOAT_FAST_INT64
)
204 changes: 200 additions & 4 deletions code_gen/cpp_gen.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
require 'Utility/helper_cpp'
require 'Utility/fp_helper_cpp'
require 'code_gen/fp_cpp_gen'
# frozen_string_literal: true

# Semantics Generator: Converts IR to C++ code
module CodeGen
class CppGenerator
include FPEmitters

attr_reader :emitter, :mapping

def initialize(emitter, mapping = {})
Expand All @@ -22,6 +26,20 @@ def binary_operation(emitter, operation, op_str)
emitter.emit_line("#{dst} = #{src1} #{op_str} #{src2};")
end

# Helpers for easier operand mapping in operations emitters
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)
Expand All @@ -36,15 +54,18 @@ def cpu_read_reg(dst)
"cpu.get#{dst[:regset]}"
end

def cpu_write_mem(addr, val)
"cpu.m_memory->write(#{addr}, #{val})"
def cpu_write_mem(addr, val, val_op)
"cpu.m_memory->write<#{Utility::HelperCpp.gen_small_type(val_op[:type])}>(#{addr}, #{val})"
end

def cpu_read_mem(dst, addr)
"cpu.m_memory->read<#{Utility::HelperCpp.gen_small_type(dst[:type])}>(#{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, '+')
Expand Down Expand Up @@ -102,7 +123,6 @@ 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]
Expand All @@ -121,21 +141,197 @@ def generate_statement(operation)
when :writeMem
addr = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name]
val = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name]
@emitter.emit_line("#{cpu_write_mem addr, val};")
@emitter.emit_line("#{cpu_write_mem addr, val, operation[:oprnds][1]};")
when :extract
dst = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name]
src = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name]

@emitter.emit_line("#{dst} = static_cast<#{Utility::HelperCpp.gen_small_type operation[:oprnds][0][:type]}>(#{src} << #{operation[:oprnds][3][:value]});")
when :sysCall
@emitter.emit_line('cpu.doExit();')
when :readCSR

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we accept it? @egorshamshura

@egorshamshura egorshamshura Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not good to create separate operation for every external call

I think we need to add something like "externCall" or just "call" operation

dst = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name]
csr = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name]
csr = csr.nil? ? operation[:oprnds][1][:value] : csr
@emitter.emit_line("#{dst} = cpu.readCSR(#{csr});")
when :writeCSR
csr = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name]
csr = csr.nil? ? operation[:oprnds][0][:value] : csr
val = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name]
val = val.nil? ? operation[:oprnds][1][:value] : val
@emitter.emit_line("cpu.writeCSR(#{csr}, #{val});")
when :select
dst = @mapping[operation[:oprnds][0][:name]] || operation[:oprnds][0][:name]
cond = @mapping[operation[:oprnds][1][:name]] || operation[:oprnds][1][:name]
true_val = @mapping[operation[:oprnds][2][:name]] || operation[:oprnds][2][:name]
false_val = @mapping[operation[:oprnds][3][:name]] || operation[:oprnds][3][:name]

@emitter.emit_line("#{dst} = #{cond} ? #{true_val} : #{false_val};")

# 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,
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],
rm: rm)

when :f32_sub then emit_fp_binary('f32_sub', operation,
dst_type: :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],
rm: rm)

when :f32_mul then emit_fp_binary('f32_mul', operation,
dst_type: :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],
rm: rm)

when :f32_div then emit_fp_binary('f32_div', operation,
dst_type: :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],
rm: rm)
when :f32_sqrt then emit_fp_unary('f32_sqrt', operation,
dst_type: :f32,
src_type: :f32,
rm: rm)

when :f64_sqrt then emit_fp_unary('f64_sqrt', operation,
dst_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],
rm: rm)

when :f64_mul_add then emit_fp_ternary('f64_mulAdd', operation,
dst_type: :f64,
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, 2],
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, 2],
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],
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],
rm: rm)

# Floating point comparisons (no rm)
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_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)
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 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,
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, rm: rm)

when :u32_to_f32 then emit_fp_unary('ui32_to_f32', operation,
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, rm: rm)

when :u64_to_f32 then emit_fp_unary('ui64_to_f32', operation,
dst_type: :f32, src_type: :u64, rm: rm)

# Classification
when :f32_classify
dst, src = map_n_operands(operation, 2)
@emitter.emit_line("#{dst} = kArchSupport.f32_classify(static_cast<uint32_t>(#{src}));")

when :f64_classify
dst, src = map_n_operands(operation, 2)
@emitter.emit_line("#{dst} = kArchSupport.f64_classify(static_cast<uint64_t>(#{src}));")

else raise "Unknown statement type: #{operation[:name]}, terminating program"
end
end
end
Expand Down
Loading
Loading