reorganized layout to only contain risc-v stuff

This commit is contained in:
2019-06-11 16:49:37 +00:00
parent eb8365f4c3
commit 67d9beb7bd
133 changed files with 1460 additions and 9277 deletions

87
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,87 @@
# library files
FILE(GLOB RiscVSCHeaders ${PROJECT_SOURCE_DIR}/incl/sysc/*.h ${PROJECT_SOURCE_DIR}/incl/sysc/*/*.h)
set(LIB_HEADERS ${RiscVSCHeaders} )
set(LIB_SOURCES
iss/rv32gc.cpp
iss/rv32imac.cpp
iss/rv64i.cpp
iss/rv64gc.cpp
internal/fp_functions.cpp
internal/vm_rv32gc.cpp
internal/vm_rv32imac.cpp
internal/vm_rv64i.cpp
internal/vm_rv64gc.cpp
plugin/instruction_count.cpp
plugin/cycle_estimate.cpp)
# Define two variables in order not to repeat ourselves.
set(LIBRARY_NAME riscv)
# Define the library
add_library(${LIBRARY_NAME} ${LIB_SOURCES})
SET(${LIBRARY_NAME} -Wl,-whole-archive -l${LIBRARY_NAME} -Wl,-no-whole-archive)
set_target_properties(${LIBRARY_NAME} PROPERTIES
VERSION ${VERSION} # ${VERSION} was defined in the main CMakeLists.
FRAMEWORK FALSE
PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers
)
#set_property(TARGET ${LIBRARY_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON)
if(SystemC_FOUND)
set(SC_LIBRARY_NAME riscv_sc)
add_library(${SC_LIBRARY_NAME} SHARED sysc/core_complex.cpp)
add_definitions(-DWITH_SYSTEMC)
include_directories(${SystemC_INCLUDE_DIRS})
include_directories(${CCI_INCLUDE_DIRS})
if(SCV_FOUND)
add_definitions(-DWITH_SCV)
include_directories(${SCV_INCLUDE_DIRS})
endif()
set_target_properties(${SC_LIBRARY_NAME} PROPERTIES
VERSION ${VERSION} # ${VERSION} was defined in the main CMakeLists.
FRAMEWORK FALSE
PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers
)
target_link_libraries(${SC_LIBRARY_NAME} ${LIBRARY_NAME})
target_link_libraries(${SC_LIBRARY_NAME} dbt-core)
target_link_libraries(${SC_LIBRARY_NAME} softfloat)
target_link_libraries(${SC_LIBRARY_NAME} sc-components)
target_link_libraries(${SC_LIBRARY_NAME} external)
target_link_libraries(${SC_LIBRARY_NAME} ${llvm_libs})
target_link_libraries(${SC_LIBRARY_NAME} ${Boost_LIBRARIES} )
endif()
# This is a make target, so you can do a "make riscv-sc"
set(APPLICATION_NAME riscv-sim)
add_executable(${APPLICATION_NAME} main.cpp)
# Links the target exe against the libraries
target_link_libraries(${APPLICATION_NAME} ${LIBRARY_NAME})
target_link_libraries(${APPLICATION_NAME} jsoncpp)
target_link_libraries(${APPLICATION_NAME} dbt-core)
target_link_libraries(${APPLICATION_NAME} softfloat)
target_link_libraries(${APPLICATION_NAME} external)
target_link_libraries(${APPLICATION_NAME} ${llvm_libs})
target_link_libraries(${APPLICATION_NAME} ${Boost_LIBRARIES} )
if (Tcmalloc_FOUND)
target_link_libraries(${APPLICATION_NAME} ${Tcmalloc_LIBRARIES})
endif(Tcmalloc_FOUND)
# Says how and where to install software
# Targets:
# * <prefix>/lib/<libraries>
# * header location after install: <prefix>/include/<project>/*.h
# * headers can be included by C++ code `#<project>/Bar.hpp>`
install(TARGETS ${LIBRARY_NAME} ${APPLICATION_NAME}
EXPORT ${PROJECT_NAME}Targets # for downstream dependencies
ARCHIVE DESTINATION lib COMPONENT libs # static lib
RUNTIME DESTINATION bin COMPONENT libs # binaries
LIBRARY DESTINATION lib COMPONENT libs # shared lib
FRAMEWORK DESTINATION bin COMPONENT libs # for mac
PUBLIC_HEADER DESTINATION incl/${PROJECT_NAME} COMPONENT devel # headers for mac (note the different component -> different package)
INCLUDES DESTINATION incl # headers
)

View File

@ -0,0 +1,514 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017, MINRES Technologies GmbH
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Contributors:
// eyck@minres.com - initial API and implementation
////////////////////////////////////////////////////////////////////////////////
#include <iss/iss.h>
#include <iss/llvm/vm_base.h>
extern "C" {
#include <softfloat.h>
#include "internals.h"
#include "specialize.h"
}
#include <limits>
namespace iss {
namespace vm {
namespace fp_impl {
using namespace std;
#define INT_TYPE(L) Type::getIntNTy(mod->getContext(), L)
#define FLOAT_TYPE Type::getFloatTy(mod->getContext())
#define DOUBLE_TYPE Type::getDoubleTy(mod->getContext())
#define VOID_TYPE Type::getVoidTy(mod->getContext())
#define THIS_PTR_TYPE Type::getIntNPtrTy(mod->getContext(), 8)
#define FDECLL(NAME, RET, ...) \
Function *NAME##_func = CurrentModule->getFunction(#NAME); \
if (!NAME##_func) { \
std::vector<Type *> NAME##_args{__VA_ARGS__}; \
FunctionType *NAME##_type = FunctionType::get(RET, NAME##_args, false); \
NAME##_func = Function::Create(NAME##_type, GlobalValue::ExternalLinkage, #NAME, CurrentModule); \
NAME##_func->setCallingConv(CallingConv::C); \
}
#define FDECL(NAME, RET, ...) \
std::vector<Type *> NAME##_args{__VA_ARGS__}; \
FunctionType *NAME##_type = llvm::FunctionType::get(RET, NAME##_args, false); \
mod->getOrInsertFunction(#NAME, NAME##_type);
using namespace llvm;
void add_fp_functions_2_module(Module *mod, uint32_t flen, uint32_t xlen) {
if(flen){
FDECL(fget_flags, INT_TYPE(32));
FDECL(fadd_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fsub_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fmul_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fdiv_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fsqrt_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fcmp_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(32));
FDECL(fcvt_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fmadd_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fsel_s, INT_TYPE(32), INT_TYPE(32), INT_TYPE(32), INT_TYPE(32));
FDECL(fclass_s, INT_TYPE(32), INT_TYPE(32));
FDECL(fcvt_32_64, INT_TYPE(64), INT_TYPE(32), INT_TYPE(32), INT_TYPE(8));
FDECL(fcvt_64_32, INT_TYPE(32), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8));
if(flen>32){
FDECL(fconv_d2f, INT_TYPE(32), INT_TYPE(64), INT_TYPE(8));
FDECL(fconv_f2d, INT_TYPE(64), INT_TYPE(32), INT_TYPE(8));
FDECL(fadd_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(8));
FDECL(fsub_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(8));
FDECL(fmul_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(8));
FDECL(fdiv_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(8));
FDECL(fsqrt_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(8));
FDECL(fcmp_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(32));
FDECL(fcvt_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8));
FDECL(fmadd_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(32), INT_TYPE(8));
FDECL(fsel_d, INT_TYPE(64), INT_TYPE(64), INT_TYPE(64), INT_TYPE(32));
FDECL(fclass_d, INT_TYPE(64), INT_TYPE(64));
FDECL(unbox_s, INT_TYPE(32), INT_TYPE(64));
}
}
}
}
}
}
using this_t = uint8_t *;
const uint8_t rmm_map[] = {
softfloat_round_near_even /*RNE*/,
softfloat_round_minMag/*RTZ*/,
softfloat_round_min/*RDN*/,
softfloat_round_max/*RUP?*/,
softfloat_round_near_maxMag /*RMM*/,
softfloat_round_max/*RTZ*/,
softfloat_round_max/*RTZ*/,
softfloat_round_max/*RTZ*/,
};
const uint32_t quiet_nan32=0x7fC00000;
extern "C" {
uint32_t fget_flags(){
return softfloat_exceptionFlags&0x1f;
}
uint32_t fadd_s(uint32_t v1, uint32_t v2, uint8_t mode) {
float32_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float32_t r =f32_add(v1f, v2f);
return r.v;
}
uint32_t fsub_s(uint32_t v1, uint32_t v2, uint8_t mode) {
float32_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float32_t r=f32_sub(v1f, v2f);
return r.v;
}
uint32_t fmul_s(uint32_t v1, uint32_t v2, uint8_t mode) {
float32_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float32_t r=f32_mul(v1f, v2f);
return r.v;
}
uint32_t fdiv_s(uint32_t v1, uint32_t v2, uint8_t mode) {
float32_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float32_t r=f32_div(v1f, v2f);
return r.v;
}
uint32_t fsqrt_s(uint32_t v1, uint8_t mode) {
float32_t v1f{v1};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float32_t r=f32_sqrt(v1f);
return r.v;
}
uint32_t fcmp_s(uint32_t v1, uint32_t v2, uint32_t op) {
float32_t v1f{v1},v2f{v2};
softfloat_exceptionFlags=0;
bool nan = (v1&defaultNaNF32UI)==quiet_nan32 || (v2&defaultNaNF32UI)==quiet_nan32;
bool snan = softfloat_isSigNaNF32UI(v1) || softfloat_isSigNaNF32UI(v2);
switch(op){
case 0:
if(nan | snan){
if(snan) softfloat_raiseFlags(softfloat_flag_invalid);
return 0;
} else
return f32_eq(v1f,v2f )?1:0;
case 1:
if(nan | snan){
softfloat_raiseFlags(softfloat_flag_invalid);
return 0;
} else
return f32_le(v1f,v2f )?1:0;
case 2:
if(nan | snan){
softfloat_raiseFlags(softfloat_flag_invalid);
return 0;
} else
return f32_lt(v1f,v2f )?1:0;
default:
break;
}
return -1;
}
uint32_t fcvt_s(uint32_t v1, uint32_t op, uint8_t mode) {
float32_t v1f{v1};
softfloat_exceptionFlags=0;
float32_t r;
switch(op){
case 0:{ //w->s, fp to int32
uint_fast32_t res = f32_to_i32(v1f,rmm_map[mode&0x7],true);
return (uint32_t)res;
}
case 1:{ //wu->s
uint_fast32_t res = f32_to_ui32(v1f,rmm_map[mode&0x7],true);
return (uint32_t)res;
}
case 2: //s->w
r=i32_to_f32(v1);
return r.v;
case 3: //s->wu
r=ui32_to_f32(v1);
return r.v;
}
return 0;
}
uint32_t fmadd_s(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t op, uint8_t mode) {
// op should be {softfloat_mulAdd_subProd(2), softfloat_mulAdd_subC(1)}
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float32_t res = softfloat_mulAddF32(v1, v2, v3, op&0x1);
if(op>1) res.v ^= 1ULL<<31;
return res.v;
}
uint32_t fsel_s(uint32_t v1, uint32_t v2, uint32_t op) {
softfloat_exceptionFlags = 0;
bool v1_nan = (v1 & defaultNaNF32UI) == defaultNaNF32UI;
bool v2_nan = (v2 & defaultNaNF32UI) == defaultNaNF32UI;
bool v1_snan = softfloat_isSigNaNF32UI(v1);
bool v2_snan = softfloat_isSigNaNF32UI(v2);
if (v1_snan || v2_snan) softfloat_raiseFlags(softfloat_flag_invalid);
if (v1_nan || v1_snan)
return (v2_nan || v2_snan) ? defaultNaNF32UI : v2;
else
if (v2_nan || v2_snan)
return v1;
else {
if ((v1 & 0x7fffffff) == 0 && (v2 & 0x7fffffff) == 0) {
return op == 0 ? ((v1 & 0x80000000) ? v1 : v2) : ((v1 & 0x80000000) ? v2 : v1);
} else {
float32_t v1f{ v1 }, v2f{ v2 };
return op == 0 ? (f32_lt(v1f, v2f) ? v1 : v2) : (f32_lt(v1f, v2f) ? v2 : v1);
}
}
}
uint32_t fclass_s( uint32_t v1 ){
float32_t a{v1};
union ui32_f32 uA;
uint_fast32_t uiA;
uA.f = a;
uiA = uA.ui;
uint_fast16_t infOrNaN = expF32UI( uiA ) == 0xFF;
uint_fast16_t subnormalOrZero = expF32UI( uiA ) == 0;
bool sign = signF32UI( uiA );
bool fracZero = fracF32UI( uiA ) == 0;
bool isNaN = isNaNF32UI( uiA );
bool isSNaN = softfloat_isSigNaNF32UI( uiA );
return
( sign && infOrNaN && fracZero ) << 0 |
( sign && !infOrNaN && !subnormalOrZero ) << 1 |
( sign && subnormalOrZero && !fracZero ) << 2 |
( sign && subnormalOrZero && fracZero ) << 3 |
( !sign && infOrNaN && fracZero ) << 7 |
( !sign && !infOrNaN && !subnormalOrZero ) << 6 |
( !sign && subnormalOrZero && !fracZero ) << 5 |
( !sign && subnormalOrZero && fracZero ) << 4 |
( isNaN && isSNaN ) << 8 |
( isNaN && !isSNaN ) << 9;
}
uint32_t fconv_d2f(uint64_t v1, uint8_t mode){
softfloat_roundingMode=rmm_map[mode&0x7];
bool nan = (v1 & defaultNaNF64UI)==defaultNaNF64UI;
if(nan){
return defaultNaNF32UI;
} else {
float32_t res = f64_to_f32(float64_t{v1});
return res.v;
}
}
uint64_t fconv_f2d(uint32_t v1, uint8_t mode){
bool nan = (v1 & defaultNaNF32UI)==defaultNaNF32UI;
if(nan){
return defaultNaNF64UI;
} else {
softfloat_roundingMode=rmm_map[mode&0x7];
float64_t res = f32_to_f64(float32_t{v1});
return res.v;
}
}
uint64_t fadd_d(uint64_t v1, uint64_t v2, uint8_t mode) {
bool nan = (v1&defaultNaNF32UI)==quiet_nan32;
bool snan = softfloat_isSigNaNF32UI(v1);
float64_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float64_t r =f64_add(v1f, v2f);
return r.v;
}
uint64_t fsub_d(uint64_t v1, uint64_t v2, uint8_t mode) {
float64_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float64_t r=f64_sub(v1f, v2f);
return r.v;
}
uint64_t fmul_d(uint64_t v1, uint64_t v2, uint8_t mode) {
float64_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float64_t r=f64_mul(v1f, v2f);
return r.v;
}
uint64_t fdiv_d(uint64_t v1, uint64_t v2, uint8_t mode) {
float64_t v1f{v1},v2f{v2};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float64_t r=f64_div(v1f, v2f);
return r.v;
}
uint64_t fsqrt_d(uint64_t v1, uint8_t mode) {
float64_t v1f{v1};
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float64_t r=f64_sqrt(v1f);
return r.v;
}
uint64_t fcmp_d(uint64_t v1, uint64_t v2, uint32_t op) {
float64_t v1f{v1},v2f{v2};
softfloat_exceptionFlags=0;
bool nan = (v1&defaultNaNF64UI)==quiet_nan32 || (v2&defaultNaNF64UI)==quiet_nan32;
bool snan = softfloat_isSigNaNF64UI(v1) || softfloat_isSigNaNF64UI(v2);
switch(op){
case 0:
if(nan | snan){
if(snan) softfloat_raiseFlags(softfloat_flag_invalid);
return 0;
} else
return f64_eq(v1f,v2f )?1:0;
case 1:
if(nan | snan){
softfloat_raiseFlags(softfloat_flag_invalid);
return 0;
} else
return f64_le(v1f,v2f )?1:0;
case 2:
if(nan | snan){
softfloat_raiseFlags(softfloat_flag_invalid);
return 0;
} else
return f64_lt(v1f,v2f )?1:0;
default:
break;
}
return -1;
}
uint64_t fcvt_d(uint64_t v1, uint32_t op, uint8_t mode) {
float64_t v1f{v1};
softfloat_exceptionFlags=0;
float64_t r;
switch(op){
case 0:{ //l->d, fp to int32
int64_t res = f64_to_i64(v1f,rmm_map[mode&0x7],true);
return (uint64_t)res;
}
case 1:{ //lu->s
uint64_t res = f64_to_ui64(v1f,rmm_map[mode&0x7],true);
return res;
}
case 2: //s->l
r=i64_to_f64(v1);
return r.v;
case 3: //s->lu
r=ui64_to_f64(v1);
return r.v;
}
return 0;
}
uint64_t fmadd_d(uint64_t v1, uint64_t v2, uint64_t v3, uint32_t op, uint8_t mode) {
// op should be {softfloat_mulAdd_subProd(2), softfloat_mulAdd_subC(1)}
softfloat_roundingMode=rmm_map[mode&0x7];
softfloat_exceptionFlags=0;
float64_t res = softfloat_mulAddF64(v1, v2, v3, op&0x1);
if(op>1) res.v ^= 1ULL<<63;
return res.v;
}
uint64_t fsel_d(uint64_t v1, uint64_t v2, uint32_t op) {
softfloat_exceptionFlags = 0;
bool v1_nan = (v1 & defaultNaNF64UI) == defaultNaNF64UI;
bool v2_nan = (v2 & defaultNaNF64UI) == defaultNaNF64UI;
bool v1_snan = softfloat_isSigNaNF64UI(v1);
bool v2_snan = softfloat_isSigNaNF64UI(v2);
if (v1_snan || v2_snan) softfloat_raiseFlags(softfloat_flag_invalid);
if (v1_nan || v1_snan)
return (v2_nan || v2_snan) ? defaultNaNF64UI : v2;
else
if (v2_nan || v2_snan)
return v1;
else {
if ((v1 & std::numeric_limits<int64_t>::max()) == 0 && (v2 & std::numeric_limits<int64_t>::max()) == 0) {
return op == 0 ?
((v1 & std::numeric_limits<int64_t>::min()) ? v1 : v2) :
((v1 & std::numeric_limits<int64_t>::min()) ? v2 : v1);
} else {
float64_t v1f{ v1 }, v2f{ v2 };
return op == 0 ?
(f64_lt(v1f, v2f) ? v1 : v2) :
(f64_lt(v1f, v2f) ? v2 : v1);
}
}
}
uint64_t fclass_d(uint64_t v1 ){
float64_t a{v1};
union ui64_f64 uA;
uint_fast64_t uiA;
uA.f = a;
uiA = uA.ui;
uint_fast16_t infOrNaN = expF64UI( uiA ) == 0x7FF;
uint_fast16_t subnormalOrZero = expF64UI( uiA ) == 0;
bool sign = signF64UI( uiA );
bool fracZero = fracF64UI( uiA ) == 0;
bool isNaN = isNaNF64UI( uiA );
bool isSNaN = softfloat_isSigNaNF64UI( uiA );
return
( sign && infOrNaN && fracZero ) << 0 |
( sign && !infOrNaN && !subnormalOrZero ) << 1 |
( sign && subnormalOrZero && !fracZero ) << 2 |
( sign && subnormalOrZero && fracZero ) << 3 |
( !sign && infOrNaN && fracZero ) << 7 |
( !sign && !infOrNaN && !subnormalOrZero ) << 6 |
( !sign && subnormalOrZero && !fracZero ) << 5 |
( !sign && subnormalOrZero && fracZero ) << 4 |
( isNaN && isSNaN ) << 8 |
( isNaN && !isSNaN ) << 9;
}
uint64_t fcvt_32_64(uint32_t v1, uint32_t op, uint8_t mode) {
float32_t v1f{v1};
softfloat_exceptionFlags=0;
float64_t r;
switch(op){
case 0: //l->s, fp to int32
return f32_to_i64(v1f,rmm_map[mode&0x7],true);
case 1: //wu->s
return f32_to_ui64(v1f,rmm_map[mode&0x7],true);
case 2: //s->w
r=i32_to_f64(v1);
return r.v;
case 3: //s->wu
r=ui32_to_f64(v1);
return r.v;
}
return 0;
}
uint32_t fcvt_64_32(uint64_t v1, uint32_t op, uint8_t mode) {
softfloat_exceptionFlags=0;
float32_t r;
switch(op){
case 0:{ //wu->s
int32_t r=f64_to_i32(float64_t{v1}, rmm_map[mode&0x7],true);
return r;
}
case 1:{ //wu->s
uint32_t r=f64_to_ui32(float64_t{v1}, rmm_map[mode&0x7],true);
return r;
}
case 2: //l->s, fp to int32
r=i64_to_f32(v1);
return r.v;
case 3: //wu->s
r=ui64_to_f32(v1);
return r.v;
}
return 0;
}
uint32_t unbox_s(uint64_t v){
constexpr uint64_t mask = std::numeric_limits<uint64_t>::max() & ~((uint64_t)std::numeric_limits<uint32_t>::max());
if((v & mask) != mask)
return 0x7fc00000;
else
return v & std::numeric_limits<uint32_t>::max();
}
}

8893
src/internal/vm_rv32gc.cpp Normal file

File diff suppressed because it is too large Load Diff

4817
src/internal/vm_rv32imac.cpp Normal file

File diff suppressed because it is too large Load Diff

11369
src/internal/vm_rv64gc.cpp Normal file

File diff suppressed because it is too large Load Diff

3152
src/internal/vm_rv64i.cpp Normal file

File diff suppressed because it is too large Load Diff

80
src/iss/rv32gc.cpp Normal file
View File

@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
#include "util/ities.h"
#include <util/logging.h>
#include <elfio/elfio.hpp>
#include <iss/arch/rv32gc.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <ihex.h>
#ifdef __cplusplus
}
#endif
#include <fstream>
#include <cstdio>
#include <cstring>
using namespace iss::arch;
constexpr std::array<const char*, 66> iss::arch::traits<iss::arch::rv32gc>::reg_names;
constexpr std::array<const char*, 66> iss::arch::traits<iss::arch::rv32gc>::reg_aliases;
constexpr std::array<const uint32_t, 72> iss::arch::traits<iss::arch::rv32gc>::reg_bit_widths;
constexpr std::array<const uint32_t, 73> iss::arch::traits<iss::arch::rv32gc>::reg_byte_offsets;
rv32gc::rv32gc() {
reg.icount=0;
}
rv32gc::~rv32gc(){
}
void rv32gc::reset(uint64_t address) {
for(size_t i=0; i<traits<rv32gc>::NUM_REGS; ++i) set_reg(i, std::vector<uint8_t>(sizeof(traits<rv32gc>::reg_t),0));
reg.PC=address;
reg.NEXT_PC=reg.PC;
reg.trap_state=0;
reg.machine_state=0x0;
reg.icount=0;
}
uint8_t* rv32gc::get_regs_base_ptr(){
return reinterpret_cast<uint8_t*>(&reg);
}
rv32gc::phys_addr_t rv32gc::virt2phys(const iss::addr_t &pc) {
return phys_addr_t(pc); // change logical address to physical address
}

77
src/iss/rv32imac.cpp Normal file
View File

@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
#include "util/ities.h"
#include <util/logging.h>
#include <elfio/elfio.hpp>
#include <iss/arch/rv32imac.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <ihex.h>
#ifdef __cplusplus
}
#endif
#include <cstdio>
#include <cstring>
#include <fstream>
using namespace iss::arch;
constexpr std::array<const char*, 33> iss::arch::traits<iss::arch::rv32imac>::reg_names;
constexpr std::array<const char*, 33> iss::arch::traits<iss::arch::rv32imac>::reg_aliases;
constexpr std::array<const uint32_t, 39> iss::arch::traits<iss::arch::rv32imac>::reg_bit_widths;
constexpr std::array<const uint32_t, 40> iss::arch::traits<iss::arch::rv32imac>::reg_byte_offsets;
rv32imac::rv32imac() {
reg.icount = 0;
reg.machine_state = 0x3;
}
rv32imac::~rv32imac() = default;
void rv32imac::reset(uint64_t address) {
for (size_t i = 0; i < traits<rv32imac>::NUM_REGS; ++i)
set_reg(i, std::vector<uint8_t>(sizeof(traits<rv32imac>::reg_t), 0));
reg.PC = address;
reg.NEXT_PC = reg.PC;
reg.trap_state = 0;
reg.machine_state = 0x3;
}
uint8_t *rv32imac::get_regs_base_ptr() { return reinterpret_cast<uint8_t *>(&reg); }
rv32imac::phys_addr_t rv32imac::virt2phys(const iss::addr_t &pc) {
return phys_addr_t(pc); // change logical address to physical address
}

81
src/iss/rv64gc.cpp Normal file
View File

@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
#include "util/ities.h"
#include <util/logging.h>
#include <elfio/elfio.hpp>
#include <iss/arch/rv64gc.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <ihex.h>
#ifdef __cplusplus
}
#endif
#include <cstdio>
#include <cstring>
#include <fstream>
using namespace iss::arch;
constexpr std::array<const char*, 66> iss::arch::traits<iss::arch::rv64gc>::reg_names;
constexpr std::array<const char*, 66> iss::arch::traits<iss::arch::rv64gc>::reg_aliases;
constexpr std::array<const uint32_t, 72> iss::arch::traits<iss::arch::rv64gc>::reg_bit_widths;
constexpr std::array<const uint32_t, 73> iss::arch::traits<iss::arch::rv64gc>::reg_byte_offsets;
rv64gc::rv64gc() {
reg.icount = 0;
}
rv64gc::~rv64gc() = default;
void rv64gc::reset(uint64_t address) {
for(size_t i=0; i<traits<rv64gc>::NUM_REGS; ++i) set_reg(i, std::vector<uint8_t>(sizeof(traits<rv64gc>::reg_t),0));
reg.PC=address;
reg.NEXT_PC=reg.PC;
reg.trap_state=0;
reg.machine_state=0x0;
reg.icount=0;
}
uint8_t *rv64gc::get_regs_base_ptr() {
return reinterpret_cast<uint8_t*>(&reg);
}
rv64gc::phys_addr_t rv64gc::virt2phys(const iss::addr_t &pc) {
return phys_addr_t(pc); // change logical address to physical address
}

79
src/iss/rv64i.cpp Normal file
View File

@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
#include "util/ities.h"
#include <util/logging.h>
#include <elfio/elfio.hpp>
#include <iss/arch/rv64i.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <ihex.h>
#ifdef __cplusplus
}
#endif
#include <cstdio>
#include <cstring>
#include <fstream>
using namespace iss::arch;
constexpr std::array<const char*, 33> iss::arch::traits<iss::arch::rv64i>::reg_names;
constexpr std::array<const char*, 33> iss::arch::traits<iss::arch::rv64i>::reg_aliases;
constexpr std::array<const uint32_t, 39> iss::arch::traits<iss::arch::rv64i>::reg_bit_widths;
constexpr std::array<const uint32_t, 40> iss::arch::traits<iss::arch::rv64i>::reg_byte_offsets;
rv64i::rv64i() {
reg.icount = 0;
}
rv64i::~rv64i() = default;
void rv64i::reset(uint64_t address) {
for(size_t i=0; i<traits<rv64i>::NUM_REGS; ++i) set_reg(i, std::vector<uint8_t>(sizeof(traits<rv64i>::reg_t),0));
reg.PC=address;
reg.NEXT_PC=reg.PC;
reg.trap_state=0;
reg.machine_state=0x0;
reg.icount=0;
}
uint8_t *rv64i::get_regs_base_ptr() {
return reinterpret_cast<uint8_t*>(&reg);
}
rv64i::phys_addr_t rv64i::virt2phys(const iss::addr_t &pc) {
return phys_addr_t(pc); // change logical address to physical address
}

192
src/main.cpp Normal file
View File

@ -0,0 +1,192 @@
/*******************************************************************************
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
#include <iostream>
#include <iss/iss.h>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <iss/arch/riscv_hart_msu_vp.h>
#include <iss/arch/rv32imac.h>
#include <iss/arch/rv32gc.h>
#include <iss/arch/rv64gc.h>
#include <iss/arch/rv64i.h>
#include <iss/llvm/jit_helper.h>
#include <iss/log_categories.h>
#include <iss/plugin/cycle_estimate.h>
#include <iss/plugin/instruction_count.h>
namespace po = boost::program_options;
int main(int argc, char *argv[]) {
/*
* Define and parse the program options
*/
po::variables_map clim;
po::options_description desc("Options");
// clang-format off
desc.add_options()
("help,h", "Print help message")
("verbose,v", po::value<int>()->implicit_value(0), "Sets logging verbosity")
("logfile,f", po::value<std::string>(), "Sets default log file.")
("disass,d", po::value<std::string>()->implicit_value(""), "Enables disassembly")
("gdb-port,g", po::value<unsigned>()->default_value(0), "enable gdb server and specify port to use")
("instructions,i", po::value<uint64_t>()->default_value(std::numeric_limits<uint64_t>::max()), "max. number of instructions to simulate")
("reset,r", po::value<std::string>(), "reset address")
("dump-ir", "dump the intermediate representation")
("elf", po::value<std::vector<std::string>>(), "ELF file(s) to load")
("mem,m", po::value<std::string>(), "the memory input file")
("plugin,p", po::value<std::vector<std::string>>(), "plugin to activate")
("isa", po::value<std::string>()->default_value("rv32gc"), "isa to use for simulation");
// clang-format on
auto parsed = po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
try {
po::store(parsed, clim); // can throw
// --help option
if (clim.count("help")) {
std::cout << "DBT-RISE-RiscV simulator for RISC-V" << std::endl << desc << std::endl;
return 0;
}
po::notify(clim); // throws on error, so do after help in case
} catch (po::error &e) {
// there are problems
std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
std::cerr << desc << std::endl;
return 1;
}
std::vector<std::string> args = collect_unrecognized(parsed.options, po::include_positional);
if (clim.count("verbose")) {
auto l = logging::as_log_level(clim["verbose"].as<int>());
LOGGER(DEFAULT)::reporting_level() = l;
LOGGER(connection)::reporting_level() = l;
}
if (clim.count("logfile")) {
// configure the connection logger
auto f = fopen(clim["logfile"].as<std::string>().c_str(), "w");
LOG_OUTPUT(DEFAULT)::stream() = f;
LOG_OUTPUT(connection)::stream() = f;
}
std::vector<iss::vm_plugin *> plugin_list;
auto res = 0;
try {
// application code comes here //
iss::init_jit(argc, argv);
bool dump = clim.count("dump-ir");
// instantiate the simulator
std::unique_ptr<iss::vm_if> vm{nullptr};
std::unique_ptr<iss::arch_if> cpu{nullptr};
std::string isa_opt(clim["isa"].as<std::string>());
if (isa_opt=="rv64ia") {
iss::arch::rv64i* lcpu = new iss::arch::riscv_hart_msu_vp<iss::arch::rv64i>();
vm = iss::create(lcpu, clim["gdb-port"].as<unsigned>());
cpu.reset(lcpu);
} else if (isa_opt=="rv64gc") {
iss::arch::rv64gc* lcpu = new iss::arch::riscv_hart_msu_vp<iss::arch::rv64gc>();
vm = iss::create(lcpu, clim["gdb-port"].as<unsigned>());
cpu.reset(lcpu);
} else if (isa_opt=="rv32imac") {
iss::arch::rv32imac* lcpu = new iss::arch::riscv_hart_msu_vp<iss::arch::rv32imac>();
vm = iss::create(lcpu, clim["gdb-port"].as<unsigned>());
cpu.reset(lcpu);
} else if (isa_opt=="rv32gc") {
iss::arch::rv32gc* lcpu = new iss::arch::riscv_hart_msu_vp<iss::arch::rv32gc>();
vm = iss::create(lcpu, clim["gdb-port"].as<unsigned>());
cpu.reset(lcpu);
} else {
LOG(ERROR) << "Illegal argument value for '--isa': " << clim["isa"].as<std::string>() << std::endl;
return 127;
}
if (clim.count("plugin")) {
for (std::string opt_val : clim["plugin"].as<std::vector<std::string>>()) {
std::string plugin_name{opt_val};
std::string filename{"cycles.txt"};
std::size_t found = opt_val.find('=');
if (found != std::string::npos) {
plugin_name = opt_val.substr(0, found);
filename = opt_val.substr(found + 1, opt_val.size());
}
if (plugin_name == "ic") {
auto *ic_plugin = new iss::plugin::instruction_count(filename);
vm->register_plugin(*ic_plugin);
plugin_list.push_back(ic_plugin);
} else if (plugin_name == "ce") {
auto *ce_plugin = new iss::plugin::cycle_estimate(filename);
vm->register_plugin(*ce_plugin);
plugin_list.push_back(ce_plugin);
} else {
LOG(ERROR) << "Unknown plugin name: " << plugin_name << ", valid names are 'ce', 'ic'" << std::endl;
return 127;
}
}
}
if (clim.count("disass")) {
vm->setDisassEnabled(true);
LOGGER(disass)::reporting_level() = logging::INFO;
auto file_name = clim["disass"].as<std::string>();
if (file_name.length() > 0) {
LOG_OUTPUT(disass)::stream() = fopen(file_name.c_str(), "w");
LOGGER(disass)::print_time() = false;
LOGGER(disass)::print_severity() = false;
}
}
uint64_t start_address = 0;
if (clim.count("mem"))
vm->get_arch()->load_file(clim["mem"].as<std::string>(), iss::arch::traits<iss::arch::rv32imac>::MEM);
if (clim.count("elf"))
for (std::string input : clim["elf"].as<std::vector<std::string>>()) {
auto start_addr = vm->get_arch()->load_file(input);
if (start_addr.second) start_address = start_addr.first;
}
for (std::string input : args) {
auto start_addr = vm->get_arch()->load_file(input); // treat remaining arguments as elf files
if (start_addr.second) start_address = start_addr.first;
}
if (clim.count("reset")) {
auto str = clim["reset"].as<std::string>();
start_address = str.find("0x") == 0 ? std::stoull(str.substr(2), nullptr, 16) : std::stoull(str, nullptr, 10);
}
vm->reset(start_address);
auto cycles = clim["instructions"].as<uint64_t>();
res = vm->start(cycles, dump);
} catch (std::exception &e) {
LOG(ERROR) << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit"
<< std::endl;
res = 2;
}
// cleanup to let plugins report of needed
for (auto *p : plugin_list) {
delete p;
}
return res;
}

821
src/plugin/GCOV.cpp Normal file
View File

@ -0,0 +1,821 @@
//===- GCOV.cpp - LLVM coverage tool --------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// GCOV implements the interface to read and write coverage files that use
// 'gcov' format.
//
//===----------------------------------------------------------------------===//
#include "GCOV.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <system_error>
using namespace llvm;
//===----------------------------------------------------------------------===//
// GCOVFile implementation.
/// readGCNO - Read GCNO buffer.
bool GCOVFile::readGCNO(GCOVBuffer &Buffer) {
if (!Buffer.readGCNOFormat())
return false;
if (!Buffer.readGCOVVersion(Version))
return false;
if (!Buffer.readInt(Checksum))
return false;
while (true) {
if (!Buffer.readFunctionTag())
break;
auto GFun = make_unique<GCOVFunction>(*this);
if (!GFun->readGCNO(Buffer, Version))
return false;
Functions.push_back(std::move(GFun));
}
GCNOInitialized = true;
return true;
}
/// readGCDA - Read GCDA buffer. It is required that readGCDA() can only be
/// called after readGCNO().
bool GCOVFile::readGCDA(GCOVBuffer &Buffer) {
assert(GCNOInitialized && "readGCDA() can only be called after readGCNO()");
if (!Buffer.readGCDAFormat())
return false;
GCOV::GCOVVersion GCDAVersion;
if (!Buffer.readGCOVVersion(GCDAVersion))
return false;
if (Version != GCDAVersion) {
errs() << "GCOV versions do not match.\n";
return false;
}
uint32_t GCDAChecksum;
if (!Buffer.readInt(GCDAChecksum))
return false;
if (Checksum != GCDAChecksum) {
errs() << "File checksums do not match: " << Checksum
<< " != " << GCDAChecksum << ".\n";
return false;
}
for (size_t i = 0, e = Functions.size(); i < e; ++i) {
if (!Buffer.readFunctionTag()) {
errs() << "Unexpected number of functions.\n";
return false;
}
if (!Functions[i]->readGCDA(Buffer, Version))
return false;
}
if (Buffer.readObjectTag()) {
uint32_t Length;
uint32_t Dummy;
if (!Buffer.readInt(Length))
return false;
if (!Buffer.readInt(Dummy))
return false; // checksum
if (!Buffer.readInt(Dummy))
return false; // num
if (!Buffer.readInt(RunCount))
return false;
Buffer.advanceCursor(Length - 3);
}
while (Buffer.readProgramTag()) {
uint32_t Length;
if (!Buffer.readInt(Length))
return false;
Buffer.advanceCursor(Length);
++ProgramCount;
}
return true;
}
void GCOVFile::print(raw_ostream &OS) const {
for (const auto &FPtr : Functions)
FPtr->print(OS);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// dump - Dump GCOVFile content to dbgs() for debugging purposes.
LLVM_DUMP_METHOD void GCOVFile::dump() const {
print(dbgs());
}
#endif
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVFile::collectLineCounts(FileInfo &FI) {
for (const auto &FPtr : Functions)
FPtr->collectLineCounts(FI);
FI.setRunCount(RunCount);
FI.setProgramCount(ProgramCount);
}
//===----------------------------------------------------------------------===//
// GCOVFunction implementation.
/// readGCNO - Read a function from the GCNO buffer. Return false if an error
/// occurs.
bool GCOVFunction::readGCNO(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
uint32_t Dummy;
if (!Buff.readInt(Dummy))
return false; // Function header length
if (!Buff.readInt(Ident))
return false;
if (!Buff.readInt(Checksum))
return false;
if (Version != GCOV::V402) {
uint32_t CfgChecksum;
if (!Buff.readInt(CfgChecksum))
return false;
if (Parent.getChecksum() != CfgChecksum) {
errs() << "File checksums do not match: " << Parent.getChecksum()
<< " != " << CfgChecksum << " in (" << Name << ").\n";
return false;
}
}
if (!Buff.readString(Name))
return false;
if (!Buff.readString(Filename))
return false;
if (!Buff.readInt(LineNumber))
return false;
// read blocks.
if (!Buff.readBlockTag()) {
errs() << "Block tag not found.\n";
return false;
}
uint32_t BlockCount;
if (!Buff.readInt(BlockCount))
return false;
for (uint32_t i = 0, e = BlockCount; i != e; ++i) {
if (!Buff.readInt(Dummy))
return false; // Block flags;
Blocks.push_back(make_unique<GCOVBlock>(*this, i));
}
// read edges.
while (Buff.readEdgeTag()) {
uint32_t EdgeCount;
if (!Buff.readInt(EdgeCount))
return false;
EdgeCount = (EdgeCount - 1) / 2;
uint32_t BlockNo;
if (!Buff.readInt(BlockNo))
return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
<< ").\n";
return false;
}
for (uint32_t i = 0, e = EdgeCount; i != e; ++i) {
uint32_t Dst;
if (!Buff.readInt(Dst))
return false;
Edges.push_back(make_unique<GCOVEdge>(*Blocks[BlockNo], *Blocks[Dst]));
GCOVEdge *Edge = Edges.back().get();
Blocks[BlockNo]->addDstEdge(Edge);
Blocks[Dst]->addSrcEdge(Edge);
if (!Buff.readInt(Dummy))
return false; // Edge flag
}
}
// read line table.
while (Buff.readLineTag()) {
uint32_t LineTableLength;
// Read the length of this line table.
if (!Buff.readInt(LineTableLength))
return false;
uint32_t EndPos = Buff.getCursor() + LineTableLength * 4;
uint32_t BlockNo;
// Read the block number this table is associated with.
if (!Buff.readInt(BlockNo))
return false;
if (BlockNo >= BlockCount) {
errs() << "Unexpected block number: " << BlockNo << " (in " << Name
<< ").\n";
return false;
}
GCOVBlock &Block = *Blocks[BlockNo];
// Read the word that pads the beginning of the line table. This may be a
// flag of some sort, but seems to always be zero.
if (!Buff.readInt(Dummy))
return false;
// Line information starts here and continues up until the last word.
if (Buff.getCursor() != (EndPos - sizeof(uint32_t))) {
StringRef F;
// Read the source file name.
if (!Buff.readString(F))
return false;
if (Filename != F) {
errs() << "Multiple sources for a single basic block: " << Filename
<< " != " << F << " (in " << Name << ").\n";
return false;
}
// Read lines up to, but not including, the null terminator.
while (Buff.getCursor() < (EndPos - 2 * sizeof(uint32_t))) {
uint32_t Line;
if (!Buff.readInt(Line))
return false;
// Line 0 means this instruction was injected by the compiler. Skip it.
if (!Line)
continue;
Block.addLine(Line);
}
// Read the null terminator.
if (!Buff.readInt(Dummy))
return false;
}
// The last word is either a flag or padding, it isn't clear which. Skip
// over it.
if (!Buff.readInt(Dummy))
return false;
}
return true;
}
/// readGCDA - Read a function from the GCDA buffer. Return false if an error
/// occurs.
bool GCOVFunction::readGCDA(GCOVBuffer &Buff, GCOV::GCOVVersion Version) {
uint32_t HeaderLength;
if (!Buff.readInt(HeaderLength))
return false; // Function header length
uint64_t EndPos = Buff.getCursor() + HeaderLength * sizeof(uint32_t);
uint32_t GCDAIdent;
if (!Buff.readInt(GCDAIdent))
return false;
if (Ident != GCDAIdent) {
errs() << "Function identifiers do not match: " << Ident
<< " != " << GCDAIdent << " (in " << Name << ").\n";
return false;
}
uint32_t GCDAChecksum;
if (!Buff.readInt(GCDAChecksum))
return false;
if (Checksum != GCDAChecksum) {
errs() << "Function checksums do not match: " << Checksum
<< " != " << GCDAChecksum << " (in " << Name << ").\n";
return false;
}
uint32_t CfgChecksum;
if (Version != GCOV::V402) {
if (!Buff.readInt(CfgChecksum))
return false;
if (Parent.getChecksum() != CfgChecksum) {
errs() << "File checksums do not match: " << Parent.getChecksum()
<< " != " << CfgChecksum << " (in " << Name << ").\n";
return false;
}
}
if (Buff.getCursor() < EndPos) {
StringRef GCDAName;
if (!Buff.readString(GCDAName))
return false;
if (Name != GCDAName) {
errs() << "Function names do not match: " << Name << " != " << GCDAName
<< ".\n";
return false;
}
}
if (!Buff.readArcTag()) {
errs() << "Arc tag not found (in " << Name << ").\n";
return false;
}
uint32_t Count;
if (!Buff.readInt(Count))
return false;
Count /= 2;
// This for loop adds the counts for each block. A second nested loop is
// required to combine the edge counts that are contained in the GCDA file.
for (uint32_t BlockNo = 0; Count > 0; ++BlockNo) {
// The last block is always reserved for exit block
if (BlockNo >= Blocks.size()) {
errs() << "Unexpected number of edges (in " << Name << ").\n";
return false;
}
if (BlockNo == Blocks.size() - 1)
errs() << "(" << Name << ") has arcs from exit block.\n";
GCOVBlock &Block = *Blocks[BlockNo];
for (size_t EdgeNo = 0, End = Block.getNumDstEdges(); EdgeNo < End;
++EdgeNo) {
if (Count == 0) {
errs() << "Unexpected number of edges (in " << Name << ").\n";
return false;
}
uint64_t ArcCount;
if (!Buff.readInt64(ArcCount))
return false;
Block.addCount(EdgeNo, ArcCount);
--Count;
}
Block.sortDstEdges();
}
return true;
}
/// getEntryCount - Get the number of times the function was called by
/// retrieving the entry block's count.
uint64_t GCOVFunction::getEntryCount() const {
return Blocks.front()->getCount();
}
/// getExitCount - Get the number of times the function returned by retrieving
/// the exit block's count.
uint64_t GCOVFunction::getExitCount() const {
return Blocks.back()->getCount();
}
void GCOVFunction::print(raw_ostream &OS) const {
OS << "===== " << Name << " (" << Ident << ") @ " << Filename << ":"
<< LineNumber << "\n";
for (const auto &Block : Blocks)
Block->print(OS);
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// dump - Dump GCOVFunction content to dbgs() for debugging purposes.
LLVM_DUMP_METHOD void GCOVFunction::dump() const {
print(dbgs());
}
#endif
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVFunction::collectLineCounts(FileInfo &FI) {
// If the line number is zero, this is a function that doesn't actually appear
// in the source file, so there isn't anything we can do with it.
if (LineNumber == 0)
return;
for (const auto &Block : Blocks)
Block->collectLineCounts(FI);
FI.addFunctionLine(Filename, LineNumber, this);
}
//===----------------------------------------------------------------------===//
// GCOVBlock implementation.
/// ~GCOVBlock - Delete GCOVBlock and its content.
GCOVBlock::~GCOVBlock() {
SrcEdges.clear();
DstEdges.clear();
Lines.clear();
}
/// addCount - Add to block counter while storing the edge count. If the
/// destination has no outgoing edges, also update that block's count too.
void GCOVBlock::addCount(size_t DstEdgeNo, uint64_t N) {
assert(DstEdgeNo < DstEdges.size()); // up to caller to ensure EdgeNo is valid
DstEdges[DstEdgeNo]->Count = N;
Counter += N;
if (!DstEdges[DstEdgeNo]->Dst.getNumDstEdges())
DstEdges[DstEdgeNo]->Dst.Counter += N;
}
/// sortDstEdges - Sort destination edges by block number, nop if already
/// sorted. This is required for printing branch info in the correct order.
void GCOVBlock::sortDstEdges() {
if (!DstEdgesAreSorted) {
SortDstEdgesFunctor SortEdges;
std::stable_sort(DstEdges.begin(), DstEdges.end(), SortEdges);
}
}
/// collectLineCounts - Collect line counts. This must be used after
/// reading .gcno and .gcda files.
void GCOVBlock::collectLineCounts(FileInfo &FI) {
for (uint32_t N : Lines)
FI.addBlockLine(Parent.getFilename(), N, this);
}
void GCOVBlock::print(raw_ostream &OS) const {
OS << "Block : " << Number << " Counter : " << Counter << "\n";
if (!SrcEdges.empty()) {
OS << "\tSource Edges : ";
for (const GCOVEdge *Edge : SrcEdges)
OS << Edge->Src.Number << " (" << Edge->Count << "), ";
OS << "\n";
}
if (!DstEdges.empty()) {
OS << "\tDestination Edges : ";
for (const GCOVEdge *Edge : DstEdges)
OS << Edge->Dst.Number << " (" << Edge->Count << "), ";
OS << "\n";
}
if (!Lines.empty()) {
OS << "\tLines : ";
for (uint32_t N : Lines)
OS << (N) << ",";
OS << "\n";
}
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
/// dump - Dump GCOVBlock content to dbgs() for debugging purposes.
LLVM_DUMP_METHOD void GCOVBlock::dump() const {
print(dbgs());
}
#endif
//===----------------------------------------------------------------------===//
// FileInfo implementation.
// Safe integer division, returns 0 if numerator is 0.
static uint32_t safeDiv(uint64_t Numerator, uint64_t Divisor) {
if (!Numerator)
return 0;
return Numerator / Divisor;
}
// This custom division function mimics gcov's branch ouputs:
// - Round to closest whole number
// - Only output 0% or 100% if it's exactly that value
static uint32_t branchDiv(uint64_t Numerator, uint64_t Divisor) {
if (!Numerator)
return 0;
if (Numerator == Divisor)
return 100;
uint8_t Res = (Numerator * 100 + Divisor / 2) / Divisor;
if (Res == 0)
return 1;
if (Res == 100)
return 99;
return Res;
}
namespace {
struct formatBranchInfo {
formatBranchInfo(const GCOV::Options &Options, uint64_t Count, uint64_t Total)
: Options(Options), Count(Count), Total(Total) {}
void print(raw_ostream &OS) const {
if (!Total)
OS << "never executed";
else if (Options.BranchCount)
OS << "taken " << Count;
else
OS << "taken " << branchDiv(Count, Total) << "%";
}
const GCOV::Options &Options;
uint64_t Count;
uint64_t Total;
};
static raw_ostream &operator<<(raw_ostream &OS, const formatBranchInfo &FBI) {
FBI.print(OS);
return OS;
}
class LineConsumer {
std::unique_ptr<MemoryBuffer> Buffer;
StringRef Remaining;
public:
LineConsumer(StringRef Filename) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
MemoryBuffer::getFileOrSTDIN(Filename);
if (std::error_code EC = BufferOrErr.getError()) {
errs() << Filename << ": " << EC.message() << "\n";
Remaining = "";
} else {
Buffer = std::move(BufferOrErr.get());
Remaining = Buffer->getBuffer();
}
}
bool empty() { return Remaining.empty(); }
void printNext(raw_ostream &OS, uint32_t LineNum) {
StringRef Line;
if (empty())
Line = "/*EOF*/";
else
std::tie(Line, Remaining) = Remaining.split("\n");
OS << format("%5u:", LineNum) << Line << "\n";
}
};
} // end anonymous namespace
/// Convert a path to a gcov filename. If PreservePaths is true, this
/// translates "/" to "#", ".." to "^", and drops ".", to match gcov.
static std::string mangleCoveragePath(StringRef Filename, bool PreservePaths) {
if (!PreservePaths)
return sys::path::filename(Filename).str();
// This behaviour is defined by gcov in terms of text replacements, so it's
// not likely to do anything useful on filesystems with different textual
// conventions.
llvm::SmallString<256> Result("");
StringRef::iterator I, S, E;
for (I = S = Filename.begin(), E = Filename.end(); I != E; ++I) {
if (*I != '/')
continue;
if (I - S == 1 && *S == '.') {
// ".", the current directory, is skipped.
} else if (I - S == 2 && *S == '.' && *(S + 1) == '.') {
// "..", the parent directory, is replaced with "^".
Result.append("^#");
} else {
if (S < I)
// Leave other components intact,
Result.append(S, I);
// And separate with "#".
Result.push_back('#');
}
S = I + 1;
}
if (S < I)
Result.append(S, I);
return Result.str();
}
std::string FileInfo::getCoveragePath(StringRef Filename,
StringRef MainFilename) {
if (Options.NoOutput)
// This is probably a bug in gcov, but when -n is specified, paths aren't
// mangled at all, and the -l and -p options are ignored. Here, we do the
// same.
return Filename;
std::string CoveragePath;
if (Options.LongFileNames && !Filename.equals(MainFilename))
CoveragePath =
mangleCoveragePath(MainFilename, Options.PreservePaths) + "##";
CoveragePath += mangleCoveragePath(Filename, Options.PreservePaths) + ".gcov";
return CoveragePath;
}
std::unique_ptr<raw_ostream>
FileInfo::openCoveragePath(StringRef CoveragePath) {
if (Options.NoOutput)
return llvm::make_unique<raw_null_ostream>();
std::error_code EC;
auto OS = llvm::make_unique<raw_fd_ostream>(CoveragePath, EC,
sys::fs::F_Text);
if (EC) {
errs() << EC.message() << "\n";
return llvm::make_unique<raw_null_ostream>();
}
return std::move(OS);
}
/// print - Print source files with collected line count information.
void FileInfo::print(raw_ostream &InfoOS, StringRef MainFilename,
StringRef GCNOFile, StringRef GCDAFile) {
SmallVector<StringRef, 4> Filenames;
for (const auto &LI : LineInfo)
Filenames.push_back(LI.first());
std::sort(Filenames.begin(), Filenames.end());
for (StringRef Filename : Filenames) {
auto AllLines = LineConsumer(Filename);
std::string CoveragePath = getCoveragePath(Filename, MainFilename);
std::unique_ptr<raw_ostream> CovStream = openCoveragePath(CoveragePath);
raw_ostream &CovOS = *CovStream;
CovOS << " -: 0:Source:" << Filename << "\n";
CovOS << " -: 0:Graph:" << GCNOFile << "\n";
CovOS << " -: 0:Data:" << GCDAFile << "\n";
CovOS << " -: 0:Runs:" << RunCount << "\n";
CovOS << " -: 0:Programs:" << ProgramCount << "\n";
const LineData &Line = LineInfo[Filename];
GCOVCoverage FileCoverage(Filename);
for (uint32_t LineIndex = 0; LineIndex < Line.LastLine || !AllLines.empty();
++LineIndex) {
if (Options.BranchInfo) {
FunctionLines::const_iterator FuncsIt = Line.Functions.find(LineIndex);
if (FuncsIt != Line.Functions.end())
printFunctionSummary(CovOS, FuncsIt->second);
}
BlockLines::const_iterator BlocksIt = Line.Blocks.find(LineIndex);
if (BlocksIt == Line.Blocks.end()) {
// No basic blocks are on this line. Not an executable line of code.
CovOS << " -:";
AllLines.printNext(CovOS, LineIndex + 1);
} else {
const BlockVector &Blocks = BlocksIt->second;
// Add up the block counts to form line counts.
DenseMap<const GCOVFunction *, bool> LineExecs;
uint64_t LineCount = 0;
for (const GCOVBlock *Block : Blocks) {
if (Options.AllBlocks) {
// Only take the highest block count for that line.
uint64_t BlockCount = Block->getCount();
LineCount = LineCount > BlockCount ? LineCount : BlockCount;
} else {
// Sum up all of the block counts.
LineCount += Block->getCount();
}
if (Options.FuncCoverage) {
// This is a slightly convoluted way to most accurately gather line
// statistics for functions. Basically what is happening is that we
// don't want to count a single line with multiple blocks more than
// once. However, we also don't simply want to give the total line
// count to every function that starts on the line. Thus, what is
// happening here are two things:
// 1) Ensure that the number of logical lines is only incremented
// once per function.
// 2) If there are multiple blocks on the same line, ensure that the
// number of lines executed is incremented as long as at least
// one of the blocks are executed.
const GCOVFunction *Function = &Block->getParent();
if (FuncCoverages.find(Function) == FuncCoverages.end()) {
std::pair<const GCOVFunction *, GCOVCoverage> KeyValue(
Function, GCOVCoverage(Function->getName()));
FuncCoverages.insert(KeyValue);
}
GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
if (LineExecs.find(Function) == LineExecs.end()) {
if (Block->getCount()) {
++FuncCoverage.LinesExec;
LineExecs[Function] = true;
} else {
LineExecs[Function] = false;
}
++FuncCoverage.LogicalLines;
} else if (!LineExecs[Function] && Block->getCount()) {
++FuncCoverage.LinesExec;
LineExecs[Function] = true;
}
}
}
if (LineCount == 0)
CovOS << " #####:";
else {
CovOS << format("%9" PRIu64 ":", LineCount);
++FileCoverage.LinesExec;
}
++FileCoverage.LogicalLines;
AllLines.printNext(CovOS, LineIndex + 1);
uint32_t BlockNo = 0;
uint32_t EdgeNo = 0;
for (const GCOVBlock *Block : Blocks) {
// Only print block and branch information at the end of the block.
if (Block->getLastLine() != LineIndex + 1)
continue;
if (Options.AllBlocks)
printBlockInfo(CovOS, *Block, LineIndex, BlockNo);
if (Options.BranchInfo) {
size_t NumEdges = Block->getNumDstEdges();
if (NumEdges > 1)
printBranchInfo(CovOS, *Block, FileCoverage, EdgeNo);
else if (Options.UncondBranch && NumEdges == 1)
printUncondBranchInfo(CovOS, EdgeNo,
(*Block->dst_begin())->Count);
}
}
}
}
FileCoverages.push_back(std::make_pair(CoveragePath, FileCoverage));
}
// FIXME: There is no way to detect calls given current instrumentation.
if (Options.FuncCoverage)
printFuncCoverage(InfoOS);
printFileCoverage(InfoOS);
}
/// printFunctionSummary - Print function and block summary.
void FileInfo::printFunctionSummary(raw_ostream &OS,
const FunctionVector &Funcs) const {
for (const GCOVFunction *Func : Funcs) {
uint64_t EntryCount = Func->getEntryCount();
uint32_t BlocksExec = 0;
for (const GCOVBlock &Block : Func->blocks())
if (Block.getNumDstEdges() && Block.getCount())
++BlocksExec;
OS << "function " << Func->getName() << " called " << EntryCount
<< " returned " << safeDiv(Func->getExitCount() * 100, EntryCount)
<< "% blocks executed "
<< safeDiv(BlocksExec * 100, Func->getNumBlocks() - 1) << "%\n";
}
}
/// printBlockInfo - Output counts for each block.
void FileInfo::printBlockInfo(raw_ostream &OS, const GCOVBlock &Block,
uint32_t LineIndex, uint32_t &BlockNo) const {
if (Block.getCount() == 0)
OS << " $$$$$:";
else
OS << format("%9" PRIu64 ":", Block.getCount());
OS << format("%5u-block %2u\n", LineIndex + 1, BlockNo++);
}
/// printBranchInfo - Print conditional branch probabilities.
void FileInfo::printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
GCOVCoverage &Coverage, uint32_t &EdgeNo) {
SmallVector<uint64_t, 16> BranchCounts;
uint64_t TotalCounts = 0;
for (const GCOVEdge *Edge : Block.dsts()) {
BranchCounts.push_back(Edge->Count);
TotalCounts += Edge->Count;
if (Block.getCount())
++Coverage.BranchesExec;
if (Edge->Count)
++Coverage.BranchesTaken;
++Coverage.Branches;
if (Options.FuncCoverage) {
const GCOVFunction *Function = &Block.getParent();
GCOVCoverage &FuncCoverage = FuncCoverages.find(Function)->second;
if (Block.getCount())
++FuncCoverage.BranchesExec;
if (Edge->Count)
++FuncCoverage.BranchesTaken;
++FuncCoverage.Branches;
}
}
for (uint64_t N : BranchCounts)
OS << format("branch %2u ", EdgeNo++)
<< formatBranchInfo(Options, N, TotalCounts) << "\n";
}
/// printUncondBranchInfo - Print unconditional branch probabilities.
void FileInfo::printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
uint64_t Count) const {
OS << format("unconditional %2u ", EdgeNo++)
<< formatBranchInfo(Options, Count, Count) << "\n";
}
// printCoverage - Print generic coverage info used by both printFuncCoverage
// and printFileCoverage.
void FileInfo::printCoverage(raw_ostream &OS,
const GCOVCoverage &Coverage) const {
OS << format("Lines executed:%.2f%% of %u\n",
double(Coverage.LinesExec) * 100 / Coverage.LogicalLines,
Coverage.LogicalLines);
if (Options.BranchInfo) {
if (Coverage.Branches) {
OS << format("Branches executed:%.2f%% of %u\n",
double(Coverage.BranchesExec) * 100 / Coverage.Branches,
Coverage.Branches);
OS << format("Taken at least once:%.2f%% of %u\n",
double(Coverage.BranchesTaken) * 100 / Coverage.Branches,
Coverage.Branches);
} else {
OS << "No branches\n";
}
OS << "No calls\n"; // to be consistent with gcov
}
}
// printFuncCoverage - Print per-function coverage info.
void FileInfo::printFuncCoverage(raw_ostream &OS) const {
for (const auto &FC : FuncCoverages) {
const GCOVCoverage &Coverage = FC.second;
OS << "Function '" << Coverage.Name << "'\n";
printCoverage(OS, Coverage);
OS << "\n";
}
}
// printFileCoverage - Print per-file coverage info.
void FileInfo::printFileCoverage(raw_ostream &OS) const {
for (const auto &FC : FileCoverages) {
const std::string &Filename = FC.first;
const GCOVCoverage &Coverage = FC.second;
OS << "File '" << Coverage.Name << "'\n";
printCoverage(OS, Coverage);
if (!Options.NoOutput)
OS << Coverage.Name << ":creating '" << Filename << "'\n";
OS << "\n";
}
}

460
src/plugin/GCOV.h Normal file
View File

@ -0,0 +1,460 @@
//===- GCOV.h - LLVM coverage tool ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This header provides the interface to read and write coverage files that
// use 'gcov' format.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_GCOV_H
#define LLVM_PROFILEDATA_GCOV_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
namespace llvm {
class GCOVFunction;
class GCOVBlock;
class FileInfo;
namespace GCOV {
enum GCOVVersion { V402, V404, V704 };
/// \brief A struct for passing gcov options between functions.
struct Options {
Options(bool A, bool B, bool C, bool F, bool P, bool U, bool L, bool N)
: AllBlocks(A), BranchInfo(B), BranchCount(C), FuncCoverage(F),
PreservePaths(P), UncondBranch(U), LongFileNames(L), NoOutput(N) {}
bool AllBlocks;
bool BranchInfo;
bool BranchCount;
bool FuncCoverage;
bool PreservePaths;
bool UncondBranch;
bool LongFileNames;
bool NoOutput;
};
} // end namespace GCOV
/// GCOVBuffer - A wrapper around MemoryBuffer to provide GCOV specific
/// read operations.
class GCOVBuffer {
public:
GCOVBuffer(MemoryBuffer *B) : Buffer(B) {}
/// readGCNOFormat - Check GCNO signature is valid at the beginning of buffer.
bool readGCNOFormat() {
StringRef File = Buffer->getBuffer().slice(0, 4);
if (File != "oncg") {
errs() << "Unexpected file type: " << File << ".\n";
return false;
}
Cursor = 4;
return true;
}
/// readGCDAFormat - Check GCDA signature is valid at the beginning of buffer.
bool readGCDAFormat() {
StringRef File = Buffer->getBuffer().slice(0, 4);
if (File != "adcg") {
errs() << "Unexpected file type: " << File << ".\n";
return false;
}
Cursor = 4;
return true;
}
/// readGCOVVersion - Read GCOV version.
bool readGCOVVersion(GCOV::GCOVVersion &Version) {
StringRef VersionStr = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (VersionStr == "*204") {
Cursor += 4;
Version = GCOV::V402;
return true;
}
if (VersionStr == "*404") {
Cursor += 4;
Version = GCOV::V404;
return true;
}
if (VersionStr == "*704") {
Cursor += 4;
Version = GCOV::V704;
return true;
}
errs() << "Unexpected version: " << VersionStr << ".\n";
return false;
}
/// readFunctionTag - If cursor points to a function tag then increment the
/// cursor and return true otherwise return false.
bool readFunctionTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
Tag[3] != '\1') {
return false;
}
Cursor += 4;
return true;
}
/// readBlockTag - If cursor points to a block tag then increment the
/// cursor and return true otherwise return false.
bool readBlockTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x41' ||
Tag[3] != '\x01') {
return false;
}
Cursor += 4;
return true;
}
/// readEdgeTag - If cursor points to an edge tag then increment the
/// cursor and return true otherwise return false.
bool readEdgeTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x43' ||
Tag[3] != '\x01') {
return false;
}
Cursor += 4;
return true;
}
/// readLineTag - If cursor points to a line tag then increment the
/// cursor and return true otherwise return false.
bool readLineTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\x45' ||
Tag[3] != '\x01') {
return false;
}
Cursor += 4;
return true;
}
/// readArcTag - If cursor points to an gcda arc tag then increment the
/// cursor and return true otherwise return false.
bool readArcTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\xa1' ||
Tag[3] != '\1') {
return false;
}
Cursor += 4;
return true;
}
/// readObjectTag - If cursor points to an object summary tag then increment
/// the cursor and return true otherwise return false.
bool readObjectTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
Tag[3] != '\xa1') {
return false;
}
Cursor += 4;
return true;
}
/// readProgramTag - If cursor points to a program summary tag then increment
/// the cursor and return true otherwise return false.
bool readProgramTag() {
StringRef Tag = Buffer->getBuffer().slice(Cursor, Cursor + 4);
if (Tag.empty() || Tag[0] != '\0' || Tag[1] != '\0' || Tag[2] != '\0' ||
Tag[3] != '\xa3') {
return false;
}
Cursor += 4;
return true;
}
bool readInt(uint32_t &Val) {
if (Buffer->getBuffer().size() < Cursor + 4) {
errs() << "Unexpected end of memory buffer: " << Cursor + 4 << ".\n";
return false;
}
StringRef Str = Buffer->getBuffer().slice(Cursor, Cursor + 4);
Cursor += 4;
Val = *(const uint32_t *)(Str.data());
return true;
}
bool readInt64(uint64_t &Val) {
uint32_t Lo, Hi;
if (!readInt(Lo) || !readInt(Hi))
return false;
Val = ((uint64_t)Hi << 32) | Lo;
return true;
}
bool readString(StringRef &Str) {
uint32_t Len = 0;
// Keep reading until we find a non-zero length. This emulates gcov's
// behaviour, which appears to do the same.
while (Len == 0)
if (!readInt(Len))
return false;
Len *= 4;
if (Buffer->getBuffer().size() < Cursor + Len) {
errs() << "Unexpected end of memory buffer: " << Cursor + Len << ".\n";
return false;
}
Str = Buffer->getBuffer().slice(Cursor, Cursor + Len).split('\0').first;
Cursor += Len;
return true;
}
uint64_t getCursor() const { return Cursor; }
void advanceCursor(uint32_t n) { Cursor += n * 4; }
private:
MemoryBuffer *Buffer;
uint64_t Cursor = 0;
};
/// GCOVFile - Collects coverage information for one pair of coverage file
/// (.gcno and .gcda).
class GCOVFile {
public:
GCOVFile() = default;
bool readGCNO(GCOVBuffer &Buffer);
bool readGCDA(GCOVBuffer &Buffer);
uint32_t getChecksum() const { return Checksum; }
void print(raw_ostream &OS) const;
void dump() const;
void collectLineCounts(FileInfo &FI);
private:
bool GCNOInitialized = false;
GCOV::GCOVVersion Version;
uint32_t Checksum = 0;
SmallVector<std::unique_ptr<GCOVFunction>, 16> Functions;
uint32_t RunCount = 0;
uint32_t ProgramCount = 0;
};
/// GCOVEdge - Collects edge information.
struct GCOVEdge {
GCOVEdge(GCOVBlock &S, GCOVBlock &D) : Src(S), Dst(D) {}
GCOVBlock &Src;
GCOVBlock &Dst;
uint64_t Count = 0;
};
/// GCOVFunction - Collects function information.
class GCOVFunction {
public:
using BlockIterator = pointee_iterator<SmallVectorImpl<
std::unique_ptr<GCOVBlock>>::const_iterator>;
GCOVFunction(GCOVFile &P) : Parent(P) {}
bool readGCNO(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
bool readGCDA(GCOVBuffer &Buffer, GCOV::GCOVVersion Version);
StringRef getName() const { return Name; }
StringRef getFilename() const { return Filename; }
size_t getNumBlocks() const { return Blocks.size(); }
uint64_t getEntryCount() const;
uint64_t getExitCount() const;
BlockIterator block_begin() const { return Blocks.begin(); }
BlockIterator block_end() const { return Blocks.end(); }
iterator_range<BlockIterator> blocks() const {
return make_range(block_begin(), block_end());
}
void print(raw_ostream &OS) const;
void dump() const;
void collectLineCounts(FileInfo &FI);
private:
GCOVFile &Parent;
uint32_t Ident = 0;
uint32_t Checksum;
uint32_t LineNumber = 0;
StringRef Name;
StringRef Filename;
SmallVector<std::unique_ptr<GCOVBlock>, 16> Blocks;
SmallVector<std::unique_ptr<GCOVEdge>, 16> Edges;
};
/// GCOVBlock - Collects block information.
class GCOVBlock {
struct EdgeWeight {
EdgeWeight(GCOVBlock *D) : Dst(D) {}
GCOVBlock *Dst;
uint64_t Count = 0;
};
struct SortDstEdgesFunctor {
bool operator()(const GCOVEdge *E1, const GCOVEdge *E2) {
return E1->Dst.Number < E2->Dst.Number;
}
};
public:
using EdgeIterator = SmallVectorImpl<GCOVEdge *>::const_iterator;
GCOVBlock(GCOVFunction &P, uint32_t N) : Parent(P), Number(N) {}
~GCOVBlock();
const GCOVFunction &getParent() const { return Parent; }
void addLine(uint32_t N) { Lines.push_back(N); }
uint32_t getLastLine() const { return Lines.back(); }
void addCount(size_t DstEdgeNo, uint64_t N);
uint64_t getCount() const { return Counter; }
void addSrcEdge(GCOVEdge *Edge) {
assert(&Edge->Dst == this); // up to caller to ensure edge is valid
SrcEdges.push_back(Edge);
}
void addDstEdge(GCOVEdge *Edge) {
assert(&Edge->Src == this); // up to caller to ensure edge is valid
// Check if adding this edge causes list to become unsorted.
if (DstEdges.size() && DstEdges.back()->Dst.Number > Edge->Dst.Number)
DstEdgesAreSorted = false;
DstEdges.push_back(Edge);
}
size_t getNumSrcEdges() const { return SrcEdges.size(); }
size_t getNumDstEdges() const { return DstEdges.size(); }
void sortDstEdges();
EdgeIterator src_begin() const { return SrcEdges.begin(); }
EdgeIterator src_end() const { return SrcEdges.end(); }
iterator_range<EdgeIterator> srcs() const {
return make_range(src_begin(), src_end());
}
EdgeIterator dst_begin() const { return DstEdges.begin(); }
EdgeIterator dst_end() const { return DstEdges.end(); }
iterator_range<EdgeIterator> dsts() const {
return make_range(dst_begin(), dst_end());
}
void print(raw_ostream &OS) const;
void dump() const;
void collectLineCounts(FileInfo &FI);
private:
GCOVFunction &Parent;
uint32_t Number;
uint64_t Counter = 0;
bool DstEdgesAreSorted = true;
SmallVector<GCOVEdge *, 16> SrcEdges;
SmallVector<GCOVEdge *, 16> DstEdges;
SmallVector<uint32_t, 16> Lines;
};
class FileInfo {
// It is unlikely--but possible--for multiple functions to be on the same
// line.
// Therefore this typedef allows LineData.Functions to store multiple
// functions
// per instance. This is rare, however, so optimize for the common case.
using FunctionVector = SmallVector<const GCOVFunction *, 1>;
using FunctionLines = DenseMap<uint32_t, FunctionVector>;
using BlockVector = SmallVector<const GCOVBlock *, 4>;
using BlockLines = DenseMap<uint32_t, BlockVector>;
struct LineData {
LineData() = default;
BlockLines Blocks;
FunctionLines Functions;
uint32_t LastLine = 0;
};
struct GCOVCoverage {
GCOVCoverage(StringRef Name) : Name(Name) {}
StringRef Name;
uint32_t LogicalLines = 0;
uint32_t LinesExec = 0;
uint32_t Branches = 0;
uint32_t BranchesExec = 0;
uint32_t BranchesTaken = 0;
};
public:
FileInfo(const GCOV::Options &Options) : Options(Options) {}
void addBlockLine(StringRef Filename, uint32_t Line, const GCOVBlock *Block) {
if (Line > LineInfo[Filename].LastLine)
LineInfo[Filename].LastLine = Line;
LineInfo[Filename].Blocks[Line - 1].push_back(Block);
}
void addFunctionLine(StringRef Filename, uint32_t Line,
const GCOVFunction *Function) {
if (Line > LineInfo[Filename].LastLine)
LineInfo[Filename].LastLine = Line;
LineInfo[Filename].Functions[Line - 1].push_back(Function);
}
void setRunCount(uint32_t Runs) { RunCount = Runs; }
void setProgramCount(uint32_t Programs) { ProgramCount = Programs; }
void print(raw_ostream &OS, StringRef MainFilename, StringRef GCNOFile,
StringRef GCDAFile);
private:
std::string getCoveragePath(StringRef Filename, StringRef MainFilename);
std::unique_ptr<raw_ostream> openCoveragePath(StringRef CoveragePath);
void printFunctionSummary(raw_ostream &OS, const FunctionVector &Funcs) const;
void printBlockInfo(raw_ostream &OS, const GCOVBlock &Block,
uint32_t LineIndex, uint32_t &BlockNo) const;
void printBranchInfo(raw_ostream &OS, const GCOVBlock &Block,
GCOVCoverage &Coverage, uint32_t &EdgeNo);
void printUncondBranchInfo(raw_ostream &OS, uint32_t &EdgeNo,
uint64_t Count) const;
void printCoverage(raw_ostream &OS, const GCOVCoverage &Coverage) const;
void printFuncCoverage(raw_ostream &OS) const;
void printFileCoverage(raw_ostream &OS) const;
const GCOV::Options &Options;
StringMap<LineData> LineInfo;
uint32_t RunCount = 0;
uint32_t ProgramCount = 0;
using FileCoverageList = SmallVector<std::pair<std::string, GCOVCoverage>, 4>;
using FuncCoverageMap = MapVector<const GCOVFunction *, GCOVCoverage>;
FileCoverageList FileCoverages;
FuncCoverageMap FuncCoverages;
};
} // end namespace llvm
#endif // LLVM_SUPPORT_GCOV_H

View File

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (C) 2017, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial API and implementation
******************************************************************************/
#include "iss/plugin/cycle_estimate.h"
#include <iss/arch_if.h>
#include <util/logging.h>
#include <fstream>
iss::plugin::cycle_estimate::cycle_estimate(std::string config_file_name)
: arch_instr(nullptr)
{
if (config_file_name.length() > 0) {
std::ifstream is(config_file_name);
if (is.is_open()) {
try {
is >> root;
} catch (Json::RuntimeError &e) {
LOG(ERROR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
}
} else {
LOG(ERROR) << "Could not open input file " << config_file_name;
}
}
}
iss::plugin::cycle_estimate::~cycle_estimate() {
}
bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if& vm) {
arch_instr = vm.get_arch()->get_instrumentation_if();
if(!arch_instr) return false;
const std::string core_name = arch_instr->core_type_name();
Json::Value &val = root[core_name];
if(!val.isNull() && val.isArray()){
delays.reserve(val.size());
for(auto it:val){
auto name = it["name"];
auto size = it["size"];
auto delay = it["delay"];
if(!name.isString() || !size.isUInt() || !(delay.isUInt() || delay.isArray())) throw std::runtime_error("JSON parse error");
if(delay.isUInt()){
delays.push_back(instr_desc{size.asUInt(), delay.asUInt(), 0});
} else {
delays.push_back(instr_desc{size.asUInt(), delay[0].asUInt(), delay[1].asUInt()});
}
}
} else {
LOG(ERROR)<<"plugin cycle_estimate: could not find an entry for "<<core_name<<" in JSON file"<<std::endl;
}
return true;
}
void iss::plugin::cycle_estimate::callback(instr_info_t instr_info) {
assert(arch_instr && "No instrumentation interface available but callback executed");
auto entry = delays[instr_info.instr_id];
bool taken = (arch_instr->get_next_pc()-arch_instr->get_pc()) != (entry.size/8);
uint32_t delay = taken ? entry.taken : entry.not_taken;
if(delay>1) arch_instr->set_curr_instr_cycles(delay);
}

View File

@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (C) 2017, MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial API and implementation
******************************************************************************/
#include "iss/plugin/instruction_count.h"
#include "iss/instrumentation_if.h"
#include <iss/arch_if.h>
#include <util/logging.h>
#include <fstream>
iss::plugin::instruction_count::instruction_count(std::string config_file_name) {
if (config_file_name.length() > 0) {
std::ifstream is(config_file_name);
if (is.is_open()) {
try {
is >> root;
} catch (Json::RuntimeError &e) {
LOG(ERROR) << "Could not parse input file " << config_file_name << ", reason: " << e.what();
}
} else {
LOG(ERROR) << "Could not open input file " << config_file_name;
}
}
}
iss::plugin::instruction_count::~instruction_count() {
size_t idx=0;
for(auto it:delays){
if(rep_counts[idx]>0)
LOG(INFO)<<it.instr_name<<";"<<rep_counts[idx];
idx++;
}
}
bool iss::plugin::instruction_count::registration(const char* const version, vm_if& vm) {
auto instr_if = vm.get_arch()->get_instrumentation_if();
if(!instr_if) return false;
const std::string core_name = instr_if->core_type_name();
Json::Value &val = root[core_name];
if(!val.isNull() && val.isArray()){
delays.reserve(val.size());
for(auto it:val){
auto name = it["name"];
auto size = it["size"];
auto delay = it["delay"];
if(!name.isString() || !size.isUInt() || !(delay.isUInt() || delay.isArray())) throw std::runtime_error("JSON parse error");
if(delay.isUInt()){
const instr_delay entry{name.asCString(), size.asUInt(), delay.asUInt(), 0};
delays.push_back(entry);
} else {
const instr_delay entry{name.asCString(), size.asUInt(), delay[0].asUInt(), delay[1].asUInt()};
delays.push_back(entry);
}
}
rep_counts.resize(delays.size());
} else {
LOG(ERROR)<<"plugin instruction_count: could not find an entry for "<<core_name<<" in JSON file"<<std::endl;
}
return true;
}
void iss::plugin::instruction_count::callback(instr_info_t instr_info) {
rep_counts[instr_info.instr_id]++;
}

483
src/sysc/core_complex.cpp Normal file
View File

@ -0,0 +1,483 @@
/*******************************************************************************
* Copyright (C) 2017, 2018 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
#include "sysc/core_complex.h"
#include "iss/arch/riscv_hart_msu_vp.h"
#include "iss/arch/rv32imac.h"
#include "iss/debugger/encoderdecoder.h"
#include "iss/debugger/gdb_session.h"
#include "iss/debugger/server.h"
#include "iss/debugger/target_adapter_if.h"
#include "iss/iss.h"
#include "iss/vm_types.h"
#include "scc/report.h"
#include <sstream>
#include <iostream>
#ifdef WITH_SCV
#include <array>
#include <scv.h>
#endif
namespace sysc {
namespace SiFive {
using namespace std;
using namespace iss;
using namespace logging;
using namespace sc_core;
namespace {
iss::debugger::encoder_decoder encdec;
}
namespace {
std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
std::array<const char*, 16> trap_str = { {
"Instruction address misaligned",
"Instruction access fault",
"Illegal instruction",
"Breakpoint",
"Load address misaligned",
"Load access fault",
"Store/AMO address misaligned",
"Store/AMO access fault",
"Environment call from U-mode",
"Environment call from S-mode",
"Reserved",
"Environment call from M-mode",
"Instruction page fault",
"Load page fault",
"Reserved",
"Store/AMO page fault"
} };
std::array<const char*, 12> irq_str = { {
"User software interrupt", "Supervisor software interrupt", "Reserved", "Machine software interrupt",
"User timer interrupt", "Supervisor timer interrupt", "Reserved", "Machine timer interrupt",
"User external interrupt", "Supervisor external interrupt", "Reserved", "Machine external interrupt" } };
}
class core_wrapper : public iss::arch::riscv_hart_msu_vp<iss::arch::rv32imac> {
public:
using core_type = arch::rv32imac;
using base_type = arch::riscv_hart_msu_vp<arch::rv32imac>;
using phys_addr_t = typename arch::traits<arch::rv32imac>::phys_addr_t;
core_wrapper(core_complex *owner)
: owner(owner) {}
uint32_t get_mode() { return this->reg.machine_state; }
inline void set_interrupt_execution(bool v) { this->interrupt_sim = v; }
inline bool get_interrupt_execution() { return this->interrupt_sim; }
base_type::hart_state<base_type::reg_t> &get_state() { return this->state; }
void notify_phase(exec_phase p) override {
if (p == ISTART) owner->sync(this->reg.icount + cycle_offset);
}
sync_type needed_sync() const override { return PRE_SYNC; }
void disass_output(uint64_t pc, const std::string instr) override {
if (INFO <= Log<Output2FILE<disass>>::reporting_level() && Output2FILE<disass>::stream()) {
std::stringstream s;
s << "[p:" << lvl[this->reg.machine_state] << ";s:0x" << std::hex << std::setfill('0')
<< std::setw(sizeof(reg_t) * 2) << (reg_t)state.mstatus << std::dec << ";c:" << this->reg.icount << "]";
Log<Output2FILE<disass>>().get(INFO, "disass")
<< "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40)
<< std::setfill(' ') << std::left << instr << s.str();
}
owner->disass_output(pc, instr);
};
status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data) override {
if (addr.access && access_type::DEBUG)
return owner->read_mem_dbg(addr.val, length, data) ? Ok : Err;
else {
return owner->read_mem(addr.val, length, data, addr.access && access_type::FETCH) ? Ok : Err;
}
}
status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data) override {
if (addr.access && access_type::DEBUG)
return owner->write_mem_dbg(addr.val, length, data) ? Ok : Err;
else {
auto res = owner->write_mem(addr.val, length, data) ? Ok : Err;
// clear MTIP on mtimecmp write
if (addr.val == 0x2004000) {
reg_t val;
this->read_csr(arch::mip, val);
if (val & (1ULL << 7)) this->write_csr(arch::mip, val & ~(1ULL << 7));
}
return res;
}
}
void wait_until(uint64_t flags) override {
SCDEBUG(owner->name()) << "Sleeping until interrupt";
do {
wait(wfi_evt);
} while (this->reg.pending_trap == 0);
base_type::wait_until(flags);
}
void local_irq(short id, bool value) {
base_type::reg_t mask = 0;
switch (id) {
case 16: // SW
mask = 1 << 3;
break;
case 17: // timer
mask = 1 << 7;
break;
case 18: // external
mask = 1 << 11;
break;
default:
/* do nothing*/
break;
}
if (value) {
this->csr[arch::mip] |= mask;
wfi_evt.notify();
} else
this->csr[arch::mip] &= ~mask;
this->check_interrupt();
}
private:
core_complex *const owner;
sc_event wfi_evt;
};
int cmd_sysc(int argc, char *argv[], debugger::out_func of, debugger::data_func df,
debugger::target_adapter_if *tgt_adapter) {
if (argc > 1) {
if (strcasecmp(argv[1], "print_time") == 0) {
std::string t = sc_time_stamp().to_string();
of(t.c_str());
std::array<char, 64> buf;
encdec.enc_string(t.c_str(), buf.data(), 63);
df(buf.data());
return Ok;
} else if (strcasecmp(argv[1], "break") == 0) {
sc_time t;
if (argc == 4) {
t = scc::parse_from_string(argv[2], argv[3]);
} else if (argc == 3) {
t = scc::parse_from_string(argv[2]);
} else
return Err;
// no check needed as it is only called if debug server is active
tgt_adapter->add_break_condition([t]() -> unsigned {
SCTRACE() << "Checking condition at " << sc_time_stamp();
return sc_time_stamp() >= t ? std::numeric_limits<unsigned>::max() : 0;
});
return Ok;
}
return Err;
}
return Err;
}
core_complex::core_complex(sc_module_name name)
: sc_module(name)
, NAMED(initiator)
, NAMED(clk_i)
, NAMED(rst_i)
, NAMED(global_irq_i)
, NAMED(timer_irq_i)
, NAMED(local_irq_i, 16)
, NAMED(elf_file, "")
, NAMED(enable_disass, false)
, NAMED(reset_address, 0ULL)
, NAMED(gdb_server_port, 0)
, NAMED(dump_ir, false)
, read_lut(tlm_dmi_ext())
, write_lut(tlm_dmi_ext())
, tgt_adapter(nullptr)
#ifdef WITH_SCV
, m_db(scv_tr_db::get_default_db())
, stream_handle(nullptr)
, instr_tr_handle(nullptr)
, fetch_tr_handle(nullptr)
#endif
{
initiator.register_invalidate_direct_mem_ptr([=](uint64_t start, uint64_t end) -> void {
auto lut_entry = read_lut.getEntry(start);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && end <= lut_entry.get_end_address() + 1) {
read_lut.removeEntry(lut_entry);
}
lut_entry = write_lut.getEntry(start);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE && end <= lut_entry.get_end_address() + 1) {
write_lut.removeEntry(lut_entry);
}
});
SC_THREAD(run);
SC_METHOD(clk_cb);
sensitive << clk_i;
SC_METHOD(rst_cb);
sensitive << rst_i;
SC_METHOD(sw_irq_cb);
sensitive << sw_irq_i;
SC_METHOD(timer_irq_cb);
sensitive << timer_irq_i;
SC_METHOD(global_irq_cb);
sensitive << global_irq_i;
}
core_complex::~core_complex() = default;
void core_complex::trace(sc_trace_file *trf) const {}
void core_complex::before_end_of_elaboration() {
cpu = std::make_unique<core_wrapper>(this);
vm = create<arch::rv32imac>(cpu.get(), gdb_server_port.get_value(), dump_ir.get_value());
#ifdef WITH_SCV
vm->setDisassEnabled(enable_disass.get_value() || m_db != nullptr);
#else
vm->setDisassEnabled(enable_disass.get_value());
#endif
auto *srv = debugger::server<debugger::gdb_session>::get();
if (srv) tgt_adapter = srv->get_target();
if (tgt_adapter)
tgt_adapter->add_custom_command(
{"sysc", [this](int argc, char *argv[], debugger::out_func of,
debugger::data_func df) -> int { return cmd_sysc(argc, argv, of, df, tgt_adapter); },
"SystemC sub-commands: break <time>, print_time"});
}
void core_complex::start_of_simulation() {
quantum_keeper.reset();
if (elf_file.get_value().size() > 0) {
istringstream is(elf_file.get_value());
string s;
while (getline(is, s, ',')) {
std::pair<uint64_t, bool> start_addr = cpu->load_file(s);
if (reset_address.is_default_value() && start_addr.second == true)
reset_address.set_value(start_addr.first);
}
}
#ifdef WITH_SCV
if (m_db != nullptr && stream_handle == nullptr) {
string basename(this->name());
stream_handle = new scv_tr_stream((basename + ".instr").c_str(), "TRANSACTOR", m_db);
instr_tr_handle = new scv_tr_generator<>("execute", *stream_handle);
fetch_tr_handle = new scv_tr_generator<uint64_t>("fetch", *stream_handle);
}
#endif
}
void core_complex::disass_output(uint64_t pc, const std::string instr_str) {
#ifdef WITH_SCV
if (m_db == nullptr) return;
if (tr_handle.is_active()) tr_handle.end_transaction();
tr_handle = instr_tr_handle->begin_transaction();
tr_handle.record_attribute("PC", pc);
tr_handle.record_attribute("INSTR", instr_str);
tr_handle.record_attribute("MODE", lvl[cpu->get_mode()]);
tr_handle.record_attribute("MSTATUS", cpu->get_state().mstatus.st.value);
tr_handle.record_attribute("LTIME_START", quantum_keeper.get_current_time().value() / 1000);
#endif
}
void core_complex::clk_cb() {
curr_clk = clk_i.read();
if (curr_clk == SC_ZERO_TIME) cpu->set_interrupt_execution(true);
}
void core_complex::rst_cb() {
if (rst_i.read()) cpu->set_interrupt_execution(true);
}
void core_complex::sw_irq_cb() { cpu->local_irq(16, sw_irq_i.read()); }
void core_complex::timer_irq_cb() { cpu->local_irq(17, timer_irq_i.read()); }
void core_complex::global_irq_cb() { cpu->local_irq(18, global_irq_i.read()); }
void core_complex::run() {
wait(SC_ZERO_TIME); // separate from elaboration phase
do {
if (rst_i.read()) {
cpu->reset(reset_address.get_value());
wait(rst_i.negedge_event());
}
while (clk_i.read() == SC_ZERO_TIME) {
wait(clk_i.value_changed_event());
}
cpu->set_interrupt_execution(false);
vm->start();
} while (cpu->get_interrupt_execution());
sc_stop();
}
bool core_complex::read_mem(uint64_t addr, unsigned length, uint8_t *const data, bool is_fetch) {
auto lut_entry = read_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE &&
addr + length <= lut_entry.get_end_address() + 1) {
auto offset = addr - lut_entry.get_start_address();
std::copy(lut_entry.get_dmi_ptr() + offset, lut_entry.get_dmi_ptr() + offset + length, data);
quantum_keeper.inc(lut_entry.get_read_latency());
return true;
} else {
tlm::tlm_generic_payload gp;
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
gp.set_data_ptr(data);
gp.set_data_length(length);
gp.set_streaming_width(length);
sc_time delay{quantum_keeper.get_local_time()};
#ifdef WITH_SCV
if (m_db != nullptr && tr_handle.is_valid()) {
if (is_fetch && tr_handle.is_active()) {
tr_handle.end_transaction();
}
auto preExt = new scv4tlm::tlm_recording_extension(tr_handle, this);
gp.set_extension(preExt);
}
#endif
initiator->b_transport(gp, delay);
SCTRACE(this->name()) << "read_mem(0x" << std::hex << addr << ") : " << data;
if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) {
return false;
}
if (gp.is_dmi_allowed()) {
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
tlm_dmi_ext dmi_data;
if (initiator->get_direct_mem_ptr(gp, dmi_data)) {
if (dmi_data.is_read_allowed())
read_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
if (dmi_data.is_write_allowed())
write_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
}
}
return true;
}
}
bool core_complex::write_mem(uint64_t addr, unsigned length, const uint8_t *const data) {
auto lut_entry = write_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE &&
addr + length <= lut_entry.get_end_address() + 1) {
auto offset = addr - lut_entry.get_start_address();
std::copy(data, data + length, lut_entry.get_dmi_ptr() + offset);
quantum_keeper.inc(lut_entry.get_read_latency());
return true;
} else {
write_buf.resize(length);
std::copy(data, data + length, write_buf.begin()); // need to copy as TLM does not guarantee data integrity
tlm::tlm_generic_payload gp;
gp.set_command(tlm::TLM_WRITE_COMMAND);
gp.set_address(addr);
gp.set_data_ptr(write_buf.data());
gp.set_data_length(length);
gp.set_streaming_width(length);
sc_time delay{quantum_keeper.get_local_time()};
#ifdef WITH_SCV
if (m_db != nullptr && tr_handle.is_valid()) {
auto preExt = new scv4tlm::tlm_recording_extension(tr_handle, this);
gp.set_extension(preExt);
}
#endif
initiator->b_transport(gp, delay);
quantum_keeper.set(delay);
SCTRACE() << "write_mem(0x" << std::hex << addr << ") : " << data;
if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) {
return false;
}
if (gp.is_dmi_allowed()) {
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
tlm_dmi_ext dmi_data;
if (initiator->get_direct_mem_ptr(gp, dmi_data)) {
if (dmi_data.is_read_allowed())
read_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
if (dmi_data.is_write_allowed())
write_lut.addEntry(dmi_data, dmi_data.get_start_address(),
dmi_data.get_end_address() - dmi_data.get_start_address() + 1);
}
}
return true;
}
}
bool core_complex::read_mem_dbg(uint64_t addr, unsigned length, uint8_t *const data) {
auto lut_entry = read_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE &&
addr + length <= lut_entry.get_end_address() + 1) {
auto offset = addr - lut_entry.get_start_address();
std::copy(lut_entry.get_dmi_ptr() + offset, lut_entry.get_dmi_ptr() + offset + length, data);
quantum_keeper.inc(lut_entry.get_read_latency());
return true;
} else {
tlm::tlm_generic_payload gp;
gp.set_command(tlm::TLM_READ_COMMAND);
gp.set_address(addr);
gp.set_data_ptr(data);
gp.set_data_length(length);
gp.set_streaming_width(length);
return initiator->transport_dbg(gp) == length;
}
}
bool core_complex::write_mem_dbg(uint64_t addr, unsigned length, const uint8_t *const data) {
auto lut_entry = write_lut.getEntry(addr);
if (lut_entry.get_granted_access() != tlm::tlm_dmi::DMI_ACCESS_NONE &&
addr + length <= lut_entry.get_end_address() + 1) {
auto offset = addr - lut_entry.get_start_address();
std::copy(data, data + length, lut_entry.get_dmi_ptr() + offset);
quantum_keeper.inc(lut_entry.get_read_latency());
return true;
} else {
write_buf.resize(length);
std::copy(data, data + length, write_buf.begin()); // need to copy as TLM does not guarantee data integrity
tlm::tlm_generic_payload gp;
gp.set_command(tlm::TLM_WRITE_COMMAND);
gp.set_address(addr);
gp.set_data_ptr(write_buf.data());
gp.set_data_length(length);
gp.set_streaming_width(length);
return initiator->transport_dbg(gp) == length;
}
}
} /* namespace SiFive */
} /* namespace sysc */