diff options
| author | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
|---|---|---|
| committer | Tor Aamodt <[email protected]> | 2010-07-15 18:09:46 -0800 |
| commit | 69f2911e04ffb1b19eef1fafb8c040af271f656e (patch) | |
| tree | 231d3b6bdc3a202f7c255bfcf7bf2c36e32cee9e /src/cuda-sim | |
creating branch for adding support for CUDA 3.x and Fermi
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 6829]
Diffstat (limited to 'src/cuda-sim')
| -rw-r--r-- | src/cuda-sim/Makefile | 170 | ||||
| -rw-r--r-- | src/cuda-sim/cuda-math.h | 81 | ||||
| -rw-r--r-- | src/cuda-sim/cuda-sim.cc | 2195 | ||||
| -rw-r--r-- | src/cuda-sim/dram_callback.h | 84 | ||||
| -rw-r--r-- | src/cuda-sim/instructions.cc | 2921 | ||||
| -rw-r--r-- | src/cuda-sim/memory.cc | 154 | ||||
| -rw-r--r-- | src/cuda-sim/memory.h | 148 | ||||
| -rw-r--r-- | src/cuda-sim/opcodes.def | 120 | ||||
| -rw-r--r-- | src/cuda-sim/opcodes.h | 82 | ||||
| -rw-r--r-- | src/cuda-sim/ptx-stats.cc | 298 | ||||
| -rw-r--r-- | src/cuda-sim/ptx-stats.h | 92 | ||||
| -rw-r--r-- | src/cuda-sim/ptx.l | 336 | ||||
| -rw-r--r-- | src/cuda-sim/ptx.y | 470 | ||||
| -rw-r--r-- | src/cuda-sim/ptx_ir.cc | 1283 | ||||
| -rw-r--r-- | src/cuda-sim/ptx_ir.h | 1223 | ||||
| -rw-r--r-- | src/cuda-sim/ptx_sim.cc | 432 | ||||
| -rw-r--r-- | src/cuda-sim/ptx_sim.h | 457 | ||||
| -rw-r--r-- | src/cuda-sim/ptxinfo.l | 128 | ||||
| -rw-r--r-- | src/cuda-sim/ptxinfo.y | 140 |
19 files changed, 10814 insertions, 0 deletions
diff --git a/src/cuda-sim/Makefile b/src/cuda-sim/Makefile new file mode 100644 index 0000000..c0d245d --- /dev/null +++ b/src/cuda-sim/Makefile @@ -0,0 +1,170 @@ +# Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia +# Vancouver, BC V6T 1Z4 +# All Rights Reserved. +# +# THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE +# TERMS AND CONDITIONS. +# +# 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 OWNERS 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. +# +# NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h +# are derived from the CUDA Toolset available from http://www.nvidia.com/cuda +# (property of NVIDIA). The files benchmarks/BlackScholes/ and +# benchmarks/template/ are derived from the CUDA SDK available from +# http://www.nvidia.com/cuda (also property of NVIDIA). The files +# src/gpgpusim_entrypoint.c and src/simplesim-3.0/ are derived from the +# SimpleScalar Toolset available from http://www.simplescalar.com/ +# (property of SimpleScalar LLC) and the files src/intersim/ are derived +# from Booksim (Simulator provided with the textbook "Principles and +# Practices of Interconnection Networks" available from +# http://cva.stanford.edu/books/ppin/). As such, those files are bound by +# the corresponding legal terms and conditions set forth separately (original +# copyright notices are left in files from these sources and where we have +# modified a file our copyright notice appears before the original copyright +# notice). +# +# Using this version of GPGPU-Sim requires a complete installation of CUDA +# which is distributed seperately by NVIDIA under separate terms and +# conditions. +# +# 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 University of British Columbia nor the names of +# its contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. +# +# 5. No nonprofit user may place any restrictions on the use of this software, +# including as modified by the user, by any other authorized user. +# +# 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, +# Ali Bakhoda, George L. Yuan, at the University of British Columbia, +# Vancouver, BC V6T 1Z4 + +default: libgpgpu_ptx_sim.a + +INTEL=0 +DEBUG?=0 + +CPP = g++ $(SNOW) +CC = gcc $(SNOW) +ifeq ($(INTEL),1) + CPP = icpc + CC = icc +endif + +CC_VERSION := $(shell gcc --version | perl -ne '/gcc\s+\(.*\)\s+([0-9.]+)/ and print $$1;') +GNUC_CPP0X := $(shell gcc --version | perl -ne 'if (/gcc\s+\(.*\)\s+([0-9.]+)/){ if($$1 >= 4.3) {$$n=1} else {$$n=0;} } END { print $$n; }') + +OPT := -O3 -g3 -Wall -Wno-unused-function -Wno-sign-compare +ifeq ($(DEBUG),1) + OPT := -g3 -Wall -Wno-unused-function -Wno-sign-compare +endif +OPT += -I$(CUDAHOME)/include +OPT += -fPIC + +CXX_OPT = $(OPT) +ifeq ($(INTEL),1) + CXX_OPT += -std=c++0x +else +ifeq ($(GNUC_CPP0X),1) + CXX_OPT += -std=c++0x +endif +endif + +OBJS := instructions.o cuda-sim.o ptx_ir.o ptx_sim.o memory.o ptx-stats.o ptx.tab.o lex.ptx_.o ptxinfo.tab.o lex.ptxinfo_.o + +CUDART_VERSION:=$(shell nvcc --version | awk '/release/ {print $$5;}' | sed 's/,//' | sed 's/\./ /' | awk '{printf("%02u%02u", 10*int($$1), 10*$$2);}') + +OPT += -DCUDART_VERSION=$(CUDART_VERSION) + +libgpgpu_ptx_sim.a: $(OBJS) + ar rcs libgpgpu_ptx_sim.a ptx.tab.o lex.ptx_.o ptxinfo.tab.o lex.ptxinfo_.o $(OBJS) + +ptx.tab.o: ptx.tab.c + $(CC) -c $(OPT) -DYYDEBUG ptx.tab.c + +lex.ptx_.o: lex.ptx_.c + $(CC) -c $(OPT) lex.ptx_.c + +ptxinfo.tab.o: ptxinfo.tab.c + $(CC) -c $(OPT) -DYYDEBUG ptxinfo.tab.c + +lex.ptxinfo_.o: lex.ptxinfo_.c + $(CC) -c $(OPT) lex.ptxinfo_.c + +ptx.tab.c: + bison --name-prefix=ptx_ -v -d ptx.y + +ptxinfo.tab.c: + bison --name-prefix=ptxinfo_ -v -d ptxinfo.y + +lex.ptx_.c: + flex ptx.l + +lex.ptxinfo_.c: + flex ptxinfo.l + +clean: + rm -f *~ *.o *.gcda *.gcno *.gcov libgpgpu_ptx_sim.a \ + ptx.tab.h ptx.tab.c ptx.output lex.ptx_.c \ + ptxinfo.tab.h ptxinfo.tab.c ptxinfo.output lex.ptxinfo_.c \ + instructions.h ptx_parser_decode.def directed_tests.log + +.c.o: + $(CC) -c $(OPT) $< -o $*.o +.cc.o: + $(CPP) -c $(CXX_OPT) $< -o $*.o + +instructions.h: instructions.cc + @touch $*.h + @chmod +w $*.h + @echo "// DO NOT EDIT THIS FILE! IT IS AUTOMATICALLY GENERATED BY THE MAKEFILE (see target for instructions.h)" > $*.h + @echo "#include \"ptx_ir.h\"" >> $*.h + @echo "#ifndef instructions_h_included" >> $*.h + @echo "#define instructions_h_included" >> $*.h + @echo "#ifdef __cplusplus" >> $*.h + @echo "extern \"C\" {" >> $*.h + @echo "#endif" >> $*.h + @cat $< | grep "_impl(" | sed 's/{.*//' | sed 's/$$/;/' >> $*.h + @echo "#ifdef __cplusplus" >> $*.h + @echo "}" >> $*.h + @echo "#endif" >> $*.h + @echo "#endif" >> $*.h + @chmod -w $*.h + @echo "created instructions.h" + +ptx_parser_decode.def: ptx.tab.c +ifeq ($(shell uname),Linux) + cat ptx.tab.h | grep "=" | sed 's/^[ ]\+//' | sed 's/[=,]//g' | sed 's/\([_A-Z1-9]\+\)[ ]\+\([0-9]\+\)/\1 \1/' | sed 's/^/DEF(/' | sed 's/ /,"/' | sed 's/$$/")/' > ptx_parser_decode.def +else + cat ptx.tab.h | grep "=" | sed -E 's/^ +//' | sed 's/[=,]//g' | sed -E 's/([_A-Z1-9]+).*/\1 \1/' | sed 's/^/DEF(/' | sed 's/ /,"/' | sed 's/$$/")/' > ptx_parser_decode.def +endif + +depend: + makedepend *.cc *.c 2> /dev/null + +instructions.o: instructions.h ptx.tab.c +ptx_ir.o: ptx.tab.c ptx_parser_decode.def + +# DO NOT DELETE + diff --git a/src/cuda-sim/cuda-math.h b/src/cuda-sim/cuda-math.h new file mode 100644 index 0000000..d495461 --- /dev/null +++ b/src/cuda-sim/cuda-math.h @@ -0,0 +1,81 @@ +// This file created from vector_types.h distributed with CUDA 1.1 +// Changes Copyright 2009, Wilson W. L. Fung and Tor M. Aamodt +// University of British Columbia + +/* + * Copyright 1993-2007 NVIDIA Corporation. All rights reserved. + * + * NOTICE TO USER: + * + * This source code is subject to NVIDIA ownership rights under U.S. and + * international Copyright laws. Users and possessors of this source code + * are hereby granted a nonexclusive, royalty-free license to use this code + * in individual and commercial software. + * + * NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE + * CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR + * IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. + * IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE + * OR PERFORMANCE OF THIS SOURCE CODE. + * + * U.S. Government End Users. This source code is a "commercial item" as + * that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of + * "commercial computer software" and "commercial computer software + * documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995) + * and is provided to the U.S. Government only as a commercial end item. + * Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through + * 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the + * source code with only those rights set forth herein. + * + * Any use of this source code in individual and commercial software must + * include, in the user documentation and internal comments to the code, + * the above Disclaimer and U.S. Government End Users Notice. + */ + +#ifndef CUDA_MATH +#define CUDA_MATH + +// cuda math implementations +#undef max +#undef min +namespace cuda_math { +#define __attribute__(a) // to remove warnings inside math_functions.h +#undef INT_MAX + +// DEVICE_BUILTIN + struct int4 { + int x, y, z, w; + }; + struct uint4 { + unsigned int x, y, z, w; + }; + struct float4 { + float x, y, z, w; + }; + struct float2 { + float x, y; + }; + + +// DEVICE_BUILTIN + typedef struct int4 int4; + typedef struct uint4 uint4; + typedef struct float4 float4; + typedef struct float2 float2; + +extern float rsqrtf(float); // CUDA 2.3 beta + +#define CUDA_FLOAT_MATH_FUNCTIONS +#include <device_types.h> +#define __CUDA_INTERNAL_COMPILATION__ +#include <math_functions.h> +#undef __CUDA_INTERNAL_COMPILATION__ +#undef __attribute__ +} + +#endif diff --git a/src/cuda-sim/cuda-sim.cc b/src/cuda-sim/cuda-sim.cc new file mode 100644 index 0000000..d691a64 --- /dev/null +++ b/src/cuda-sim/cuda-sim.cc @@ -0,0 +1,2195 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Ali Bakhoda, Wilson W. L. Fung, + * George L. Yuan, Henry Wong, Dan O'Connor, Zev Weiss and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "instructions.h" +#include "ptx_ir.h" +#include "ptx_sim.h" +#include <stdio.h> +#include <dirent.h> + +#include "opcodes.h" +#include "../intersim/statwraper.h" +#include "dram_callback.h" +#include <set> +#include <map> +#include "../util.h" +#include "memory.h" +#include "ptx-stats.h" + + +int gpgpu_ptx_instruction_classification=0; +void ** g_inst_classification_stat = NULL; +void ** g_inst_op_classification_stat= NULL; +int g_ptx_kernel_count = -1; // used for classification stat collection purposes +extern "C" int ptx_parse(); +extern "C" int ptx__scan_string(const char*); +extern "C" int ptx_debug; +extern "C" FILE *ptx_in; + +extern "C" const char *g_ptxinfo_filename = NULL; +extern "C" int ptxinfo_parse(); +extern "C" int ptxinfo_debug; +extern "C" FILE *ptxinfo_in; + +int g_debug_execution = 0; +int g_debug_thread_uid = 0; +addr_t g_debug_pc = 0xBEEF1518; +int g_override_embedded_ptx = false; + +const char *g_filename; +bool g_debug_ir_generation = false; +unsigned g_ptx_sim_num_insn = 0; + +extern memory_space *g_global_mem; +extern memory_space *g_param_mem; +extern memory_space *g_tex_mem; +extern memory_space *g_surf_mem; + +std::map<const struct textureReference*,const struct cudaArray*> TextureToArrayMap; // texture bindings +std::map<const struct textureReference*, const struct textureInfo*> TextureToInfoMap; +std::map<std::string, const struct textureReference*> NameToTextureMap; +unsigned int g_texcache_linesize; +int gpgpu_option_spread_blocks_across_cores = 0; +unsigned gpgpu_param_num_shaders = 0; + +void gpgpu_ptx_sim_bindNameToTexture(const char* name, const struct textureReference* texref) +{ + std::string texname(name); + NameToTextureMap[texname] = texref; +} + +const struct textureReference* gpgpu_ptx_sim_accessTextureofName(const char* name) { + std::string texname(name); + return NameToTextureMap[texname]; +} + +const char* gpgpu_ptx_sim_findNamefromTexture(const struct textureReference* texref) +{ + std::map<std::string, const struct textureReference*>::iterator itr = NameToTextureMap.begin(); + while (itr != NameToTextureMap.end()) { + if ((*itr).second == texref) { + const char *p = ((*itr).first).c_str(); + return p; + } + itr++; + } + return NULL; +} + +unsigned int intLOGB2( unsigned int v ) { + unsigned int shift; + unsigned int r; + + r = 0; + + shift = (( v & 0xFFFF0000) != 0 ) << 4; v >>= shift; r |= shift; + shift = (( v & 0xFF00 ) != 0 ) << 3; v >>= shift; r |= shift; + shift = (( v & 0xF0 ) != 0 ) << 2; v >>= shift; r |= shift; + shift = (( v & 0xC ) != 0 ) << 1; v >>= shift; r |= shift; + shift = (( v & 0x2 ) != 0 ) << 0; v >>= shift; r |= shift; + + return r; +} + +void gpgpu_ptx_sim_bindTextureToArray(const struct textureReference* texref, const struct cudaArray* array) +{ + TextureToArrayMap[texref] = array; + unsigned int texel_size_bits = array->desc.w + array->desc.x + array->desc.y + array->desc.z; + unsigned int texel_size = texel_size_bits/8; + unsigned int Tx, Ty; + int r; + + printf("GPGPU-Sim PTX: texel size = %d\n", texel_size); + printf("GPGPU-Sim PTX: texture cache linesize = %d\n", g_texcache_linesize); + //first determine base Tx size for given linesize + switch (g_texcache_linesize) { + case 16: + Tx = 4; + break; + case 32: + Tx = 8; + break; + case 64: + Tx = 8; + break; + case 128: + Tx = 16; + break; + case 256: + Tx = 16; + break; + default: + printf("GPGPU-Sim PTX: Line size of %d bytes currently not supported.\n", g_texcache_linesize); + assert(0); + break; + } + r = texel_size >> 2; + //modify base Tx size to take into account size of each texel in bytes + while (r != 0) { + Tx = Tx >> 1; + r = r >> 2; + } + //by now, got the correct Tx size, calculate correct Ty size + Ty = g_texcache_linesize/(Tx*texel_size); + + printf("GPGPU-Sim PTX: Tx = %d; Ty = %d, Tx_numbits = %d, Ty_numbits = %d\n", Tx, Ty, intLOGB2(Tx), intLOGB2(Ty)); + printf("GPGPU-Sim PTX: Texel size = %d bytes; texel_size_numbits = %d\n", texel_size, intLOGB2(texel_size)); + printf("GPGPU-Sim PTX: Binding texture to array starting at devPtr32 = 0x%x\n", array->devPtr32); + printf("GPGPU-Sim PTX: Texel size = %d bytes\n", texel_size); + struct textureInfo* texInfo = (struct textureInfo*) malloc(sizeof(struct textureInfo)); + texInfo->Tx = Tx; + texInfo->Ty = Ty; + texInfo->Tx_numbits = intLOGB2(Tx); + texInfo->Ty_numbits = intLOGB2(Ty); + texInfo->texel_size = texel_size; + texInfo->texel_size_numbits = intLOGB2(texel_size); + TextureToInfoMap[texref] = texInfo; +} + +const struct cudaArray* gpgpu_ptx_sim_accessArrayofTexture(struct textureReference* texref) { + return TextureToArrayMap[texref]; +} + +int gpgpu_ptx_sim_sizeofTexture(const char* name) +{ + std::string texname(name); + const struct textureReference* texref = NameToTextureMap[texname]; + const struct cudaArray* array = TextureToArrayMap[texref]; + return array->size; +} + +extern unsigned int warp_size; + +int g_warn_literal_operands_two_type_inst; + +std::list<operand_info> check_operands( int opcode, + const std::list<int> &scalar_type, + const std::list<operand_info> &operands ) +{ + if( (opcode == CVT_OP) || (opcode == SET_OP) || (opcode == SLCT_OP) || (opcode == TEX_OP) ) { + // just make sure these do not have have const operands... + if( !g_warn_literal_operands_two_type_inst ) { + std::list<operand_info>::const_iterator o; + for( o = operands.begin(); o != operands.end(); o++ ) { + const operand_info &op = *o; + if( op.is_literal() ) { + printf("GPGPU-Sim PTX: PTX uses two scalar type intruction with literal operand.\n"); + g_warn_literal_operands_two_type_inst = 1; + } + } + } + } else { + assert( scalar_type.size() < 2 ); + if( scalar_type.size() == 1 ) { + std::list<operand_info> result; + int inst_type = scalar_type.front(); + std::list<operand_info>::const_iterator o; + for( o = operands.begin(); o != operands.end(); o++ ) { + const operand_info &op = *o; + if( op.is_literal() ) { + if( (op.get_type() == double_op_t) && (inst_type == F32_TYPE) ) { + ptx_reg_t v = op.get_literal_value(); + float u = (float)v.f64; + operand_info n(u); + result.push_back(n); + } else { + result.push_back(op); + } + } else { + result.push_back(op); + } + } + return result; + } + } + return operands; +} + +ptx_instruction::ptx_instruction( int opcode, + const symbol *pred, + int neg_pred, + symbol *label, + const std::list<operand_info> &operands, + const operand_info &return_var, + const std::list<int> &options, + const std::list<int> &scalar_type, + int space_spec, + const char *file, + unsigned line, + const char *source ) +{ + m_uid = ++g_num_ptx_inst_uid; + m_PC = 0; + m_opcode = opcode; + m_pred = pred; + m_neg_pred = neg_pred; + m_label = label; + const std::list<operand_info> checked_operands = check_operands(opcode,scalar_type,operands); + m_operands.insert(m_operands.begin(), checked_operands.begin(), checked_operands.end() ); + m_return_var = return_var; + m_options = options; + m_wide = false; + m_hi = false; + m_lo = false; + m_uni = false; + m_rounding_mode = RN_OPTION; + m_compare_op = -1; + m_saturation_mode = 0; + m_geom_spec = 0; + m_vector_spec = 0; + m_atomic_spec = 0; + m_warp_size = ::warp_size; + m_membar_level = 0; + + std::list<int>::const_iterator i; + unsigned n=1; + for ( i=options.begin(); i!= options.end(); i++, n++ ) { + int last_ptx_inst_option = *i; + switch ( last_ptx_inst_option ) { + case EQU_OPTION: + case NEU_OPTION: + case LTU_OPTION: + case LEU_OPTION: + case GTU_OPTION: + case GEU_OPTION: + case EQ_OPTION: + case NE_OPTION: + case LT_OPTION: + case LE_OPTION: + case GT_OPTION: + case GE_OPTION: + case LS_OPTION: + case HS_OPTION: + m_compare_op = last_ptx_inst_option; + break; + case NUM_OPTION: + case NAN_OPTION: + m_compare_op = last_ptx_inst_option; + // assert(0); // finish this + break; + case SAT_OPTION: + m_saturation_mode = 1; + break; + case RNI_OPTION: + case RZI_OPTION: + case RMI_OPTION: + case RPI_OPTION: + case RN_OPTION: + case RZ_OPTION: + case RM_OPTION: + case RP_OPTION: + m_rounding_mode = last_ptx_inst_option; + break; + case HI_OPTION: + m_compare_op = last_ptx_inst_option; + m_hi = true; + assert( !m_lo ); + assert( !m_wide ); + break; + case LO_OPTION: + m_compare_op = last_ptx_inst_option; + m_lo = true; + assert( !m_hi ); + assert( !m_wide ); + break; + case WIDE_OPTION: + m_wide = true; + assert( !m_lo ); + assert( !m_hi ); + break; + case UNI_OPTION: + m_uni = true; // don't care... < now we DO care when constructing flowgraph> + break; + case GEOM_MODIFIER_1D: + case GEOM_MODIFIER_2D: + case GEOM_MODIFIER_3D: + m_geom_spec = last_ptx_inst_option; + break; + case V2_TYPE: + case V3_TYPE: + case V4_TYPE: + m_vector_spec = last_ptx_inst_option; + break; + case ATOMIC_AND: + case ATOMIC_OR: + case ATOMIC_XOR: + case ATOMIC_CAS: + case ATOMIC_EXCH: + case ATOMIC_ADD: + case ATOMIC_INC: + case ATOMIC_DEC: + case ATOMIC_MIN: + case ATOMIC_MAX: + m_atomic_spec = last_ptx_inst_option; + break; + case APPROX_OPTION: + break; + case FULL_OPTION: + break; + case ANY_OPTION: + m_vote_mode = vote_any; + break; + case ALL_OPTION: + m_vote_mode = vote_all; + break; + case GLOBAL_OPTION: + m_membar_level = GLOBAL_OPTION; + break; + case CTA_OPTION: + m_membar_level = CTA_OPTION; + break; + case FTZ_OPTION: + break; + + default: + assert(0); + break; + } + } + m_scalar_type = scalar_type; + m_space_spec = space_spec; + m_source_file = file?file:"<unknown>"; + m_source_line = line; + m_source = source; +} + +void ptx_instruction::print_insn() const +{ + print_insn(stdout); + fflush(stdout); +} + +void ptx_instruction::print_insn( FILE *fp ) const +{ + char buf[1024], *p; + snprintf(buf,1024,"%s", m_source.c_str()); + p = strtok(buf,";"); + if( !is_label() ) + fprintf(fp,"PC=%3u [idx=%3u] ", m_PC, m_instr_mem_index ); + else + fprintf(fp," " ); + fprintf(fp,"(%s:%u) %s", m_source_file.c_str(), m_source_line, p ); +} + +unsigned g_assemble_code_next_pc=1; +std::map<unsigned,function_info*> g_pc_to_finfo; +std::vector<ptx_instruction*> function_info::s_g_pc_to_insn; + +void function_info::print_insn( unsigned pc, FILE * fp ) const +{ + unsigned index = pc - m_start_PC; + fprintf(fp,"FUNC[%s]",m_name.c_str() ); + if ( index >= m_instr_mem_size ) { + fprintf(fp, "<past last instruction (max pc=%u)>", m_start_PC + m_instr_mem_size - 1 ); + } else { + if ( m_instr_mem[index] != NULL ) + m_instr_mem[index]->print_insn(fp); + else + fprintf(fp, "<no instruction at pc = %u>", pc ); + } +} + +void function_info::ptx_assemble() +{ + if( m_assembled ) { + return; + } + + // get the instructions into instruction memory... + unsigned num_inst = m_instructions.size(); + m_instr_mem = new ptx_instruction*[ num_inst ]; + m_instr_mem_size = num_inst; + + printf("GPGPU-Sim PTX: instruction assembly for function \'%s\'... ", m_name.c_str() ); + fflush(stdout); + std::list<ptx_instruction*>::iterator i; + addr_t n=0; // offset in m_instr_mem + addr_t PC = g_assemble_code_next_pc; // globally unique address (across functions) + m_start_PC = PC; + s_g_pc_to_insn.reserve(s_g_pc_to_insn.size() + m_instructions.size()); + for ( i=m_instructions.begin(); i != m_instructions.end(); i++ ) { + ptx_instruction *pI = *i; + if ( pI->is_label() ) { + const symbol *l = pI->get_label(); + labels[l->name()] = n; + } else { + g_pc_to_finfo[PC] = this; + m_instr_mem[n] = pI; + s_g_pc_to_insn.push_back(pI); + assert(pI == s_g_pc_to_insn[PC - 1]); + pI->set_m_instr_mem_index(n); + pI->set_PC(PC); + n++; + PC++; + } + } + g_assemble_code_next_pc=PC; + for ( unsigned ii=0; ii < n; ii++ ) { // handle branch instructions + ptx_instruction *pI = m_instr_mem[ii]; + if ( pI->get_opcode() == BRA_OP ) { + operand_info &target = pI->dst(); //get operand, e.g. target name + if ( labels.find(target.name()) == labels.end() ) { + printf("GPGPU-Sim PTX: Loader error (%s:%u): Branch label \"%s\" does not appear in assembly code.", + pI->source_file(),pI->source_line(), target.name().c_str() ); + abort(); + } + unsigned index = labels[ target.name() ]; //determine address from name + unsigned PC = m_instr_mem[index]->get_PC(); + g_current_symbol_table->set_label_address( target.get_symbol(), PC ); + target.set_type(label_t); + } + } + printf(" done.\n"); + fflush(stdout); + + create_basic_blocks(); + connect_basic_blocks(); + if ( g_debug_execution>=2 ) { + print_basic_blocks(); + print_basic_block_links(); + print_basic_block_dot(); + } + find_postdominators(); + find_ipostdominators(); + if ( g_debug_execution>=2 ) { + print_postdominators(); + print_ipostdominators(); + } + m_assembled = true; +} + + + +void gpgpu_ptx_sim_init_memory() +{ + static bool initialized = false; + if ( !initialized ) { + g_global_mem = new memory_space_impl<8192>("global",64*1024); + g_param_mem = new memory_space_impl<64>("param",64*1024); + g_tex_mem = new memory_space_impl<8192>("tex",64*1024); + g_surf_mem = new memory_space_impl<8192>("surf",64*1024); + initialized = true; + } +} + +int load_static_globals( symbol_table *symtab, unsigned min_gaddr, unsigned max_gaddr) +{ + printf( "GPGPU-Sim PTX: loading global with explicit initializers... " ); + fflush(stdout); + int ng_bytes=0; + symbol_table::iterator g=symtab->global_iterator_begin(); + bool below_image=true; + + for ( ; g!=symtab->global_iterator_end(); g++) { + symbol *global = *g; + if ( global->has_initializer() ) { + std::list<operand_info> init_list = global->get_initializer(); + for ( std::list<operand_info>::iterator i=init_list.begin(); i!=init_list.end(); i++ ) { + operand_info op = *i; + ptx_reg_t value = op.get_literal_value(); + int nbytes = 0; + switch ( op.get_type() ) { + case int_t: nbytes = 4; break; + case float_op_t: nbytes = 4; break; + case double_op_t: nbytes = 8; break; + default: + abort(); + } + unsigned addr=global->get_address(); + + assert( below_image && addr+nbytes < min_gaddr ); // min_gaddr is start of "heap" for cudaMalloc + + g_global_mem->write(addr,nbytes,&value); + ng_bytes+=nbytes; + } + } + } + printf( " done.\n"); + fflush(stdout); + return ng_bytes; +} + +int load_constants( symbol_table *symtab ) +{ + printf( "GPGPU-Sim PTX: loading constants with explicit initializers... " ); + fflush(stdout); + int nc_bytes = 0; + symbol_table::iterator g=symtab->const_iterator_begin(); + + for ( ; g!=symtab->const_iterator_end(); g++) { + symbol *constant = *g; + if ( constant->is_const() && constant->has_initializer() ) { + + // get the constant element data size + int basic_type; + size_t num_bits; + type_decode(constant->type()->get_key().scalar_type(),num_bits,basic_type); + + std::list<operand_info> init_list = constant->get_initializer(); + int nbytes_written = 0; + for ( std::list<operand_info>::iterator i=init_list.begin(); i!=init_list.end(); i++ ) { + operand_info op = *i; + ptx_reg_t value = op.get_literal_value(); + int nbytes = num_bits/8; + switch ( op.get_type() ) { + case int_t: assert(nbytes >= 1); break; + case float_op_t: assert(nbytes == 4); break; + case double_op_t: assert(nbytes >= 4); break; // account for double DEMOTING + default: + abort(); + } + unsigned addr=constant->get_address() + nbytes_written; + + g_global_mem->write(addr,nbytes,&value); // assume little endian (so u8 is the first byte in u32) + nc_bytes+=nbytes; + nbytes_written += nbytes; + } + } + } + printf( " done.\n"); + fflush(stdout); + return nc_bytes; +} + +unsigned long long g_dev_malloc=0x10000000; // start allocating from this address (lower values used for allocating globals in .ptx file) + +void* gpgpu_ptx_sim_malloc( size_t size ) +{ + unsigned long long result = g_dev_malloc; + printf("GPGPU-Sim PTX: allocating %zu bytes on GPU starting at address 0x%Lx\n", size, g_dev_malloc ); + fflush(stdout); + g_dev_malloc += size; + if (size%64) g_dev_malloc += (64 - size%64); //align to 64 byte boundaries + return(void*) result; +} + +void* gpgpu_ptx_sim_mallocarray( size_t size ) +{ + unsigned long long result = g_dev_malloc; + printf("GPGPU-Sim PTX: allocating %zu bytes on GPU starting at address 0x%Lx\n", size, g_dev_malloc ); + fflush(stdout); + g_dev_malloc += size; + if (size%64) g_dev_malloc += (64 - size%64); //align to 64 byte boundaries + return(void*) result; +} + + +void gpgpu_ptx_sim_memcpy_to_gpu( size_t dst_start_addr, const void *src, size_t count ) +{ + printf("GPGPU-Sim PTX: copying %zu bytes from CPU[0x%Lx] to GPU[0x%Lx] ... ", count, (unsigned long long) src, (unsigned long long) dst_start_addr ); + fflush(stdout); + char *src_data = (char*)src; + for (unsigned n=0; n < count; n ++ ) + g_global_mem->write(dst_start_addr+n,1, src_data+n); + printf( " done.\n"); + fflush(stdout); +} + +void gpgpu_ptx_sim_memcpy_from_gpu( void *dst, size_t src_start_addr, size_t count ) +{ + printf("GPGPU-Sim PTX: copying %zu bytes from GPU[0x%Lx] to CPU[0x%Lx] ...", count, (unsigned long long) src_start_addr, (unsigned long long) dst ); + fflush(stdout); + unsigned char *dst_data = (unsigned char*)dst; + for (unsigned n=0; n < count; n ++ ) + g_global_mem->read(src_start_addr+n,1,dst_data+n); + printf( " done.\n"); + fflush(stdout); +} + +void gpgpu_ptx_sim_memcpy_gpu_to_gpu( size_t dst, size_t src, size_t count ) +{ + printf("GPGPU-Sim PTX: copying %zu bytes from GPU[0x%Lx] to GPU[0x%Lx] ...", count, + (unsigned long long) src, (unsigned long long) dst ); + fflush(stdout); + for (unsigned n=0; n < count; n ++ ) { + unsigned char tmp; + g_global_mem->read(src+n,1,&tmp); + g_global_mem->write(dst+n,1, &tmp); + } + printf( " done.\n"); + fflush(stdout); +} + +void gpgpu_ptx_sim_memset( size_t dst_start_addr, int c, size_t count ) +{ + printf("GPGPU-Sim PTX: setting %zu bytes of memory to 0x%x starting at 0x%Lx... ", + count, (unsigned char) c, (unsigned long long) dst_start_addr ); + fflush(stdout); + unsigned char c_value = (unsigned char)c; + for (unsigned n=0; n < count; n ++ ) + g_global_mem->write(dst_start_addr+n,1,&c_value); + printf( " done.\n"); + fflush(stdout); +} + +int ptx_thread_done( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + int result = 0; + result = (the_thread==NULL) || the_thread->is_done(); + return result; +} + +const char * ptx_get_fname( unsigned PC ) +{ + static const char *null_ptr = "<null finfo ptr>"; + std::map<unsigned,function_info*>::iterator f=g_pc_to_finfo.find(PC); + if( f== g_pc_to_finfo.end() ) + return null_ptr; + return f->second->get_name().c_str(); +} + +unsigned ptx_thread_donecycle( void *thr ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thr; + if( the_thread == NULL ) + return 0; + return the_thread->donecycle(); +} + +int ptx_thread_get_next_pc( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return -1; + return the_thread->get_pc(); // PC should already be updatd to next PC at this point (was set in shader_decode() last time thread ran) +} + +void* ptx_thread_get_next_finfo( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return NULL; + return the_thread->get_finfo(); // finfo should already be updatd to next PC at this point (was set in shader_decode() last time thread ran) +} + +int ptx_thread_at_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return 0; + return the_thread->is_at_barrier(); +} + +int ptx_thread_all_at_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return 0; + return the_thread->all_at_barrier()?1:0; +} + +unsigned long long ptx_thread_get_cta_uid( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return 0; + return the_thread->get_cta_uid(); +} + +void ptx_thread_reset_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return; + the_thread->clear_barrier(); +} + +void ptx_thread_release_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return; + the_thread->release_barrier(); +} + +void ptx_print_insn( address_type pc, FILE *fp ) +{ + std::map<unsigned,function_info*>::iterator f = g_pc_to_finfo.find(pc); + if( f == g_pc_to_finfo.end() ) { + fprintf(fp,"<no instruction at address 0x%x (%u)>", pc, pc ); + return; + } + function_info *finfo = f->second; + assert( finfo ); + finfo->print_insn(pc,fp); +} + +void function_info::ptx_decode_inst( ptx_thread_info *thread, + unsigned *op_type, + int *i1, int *i2, int *i3, int *i4, + int *o1, int *o2, int *o3, int *o4, + int *vectorin, + int *vectorout, + int *arch_reg ) +{ + addr_t pc = thread->get_pc(); + unsigned index = pc - m_start_PC; + assert( index < m_instr_mem_size ); + ptx_instruction *pI = m_instr_mem[index]; //get instruction from m_instr_mem[PC] + + bool has_dst = false ; + int opcode = pI->get_opcode(); //determine the opcode + + switch ( pI->get_opcode() ) { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) case OP: has_dst = (DST!=0); break; +#include "opcodes.def" +#undef OP_DEF + default: + printf( "Execution error: Invalid opcode (0x%x)\n", pI->get_opcode() ); + break; + } + + *op_type = ALU_OP; + if ( opcode == LD_OP ) { + *op_type = LOAD_OP; + } else if ( opcode == ST_OP ) { + *op_type = STORE_OP; + } else if ( opcode == BRA_OP ) { + *op_type = BRANCH_OP; + } else if ( opcode == TEX_OP ) { + *op_type = LOAD_OP; + } else if ( opcode == ATOM_OP ) { + *op_type = LOAD_OP; // make atomics behave more like a load. + } else if ( opcode == BAR_OP ) { + *op_type = BARRIER_OP; + } + + int n=0,m=0; + ptx_instruction::const_iterator op=pI->op_iter_begin(); + for ( ; op != pI->op_iter_end(); op++, n++ ) { //process operands + + const operand_info &o = *op; + if ( has_dst && n==0 ) { + if ( o.is_reg() ) { //but is destination an actual register? (seems like it fails if it's a vector) + *o1 = o.reg_num(); + arch_reg[0] = o.arch_reg_num(); + } else if ( o.is_vector() ) { //but is destination an actual register? (seems like it fails if it's a vector) + *vectorin = 1; + *o1 = o.reg1_num(); + *o2 = o.reg2_num(); + *o3 = o.reg3_num(); + *o4 = o.reg4_num(); + for (int i = 0; i < 4; i++) + arch_reg[i] = o.arch_reg_num(i); + } + } else { + if ( o.is_reg() ) { + int reg_num = o.reg_num(); + arch_reg[m + 4] = o.arch_reg_num(); + switch ( m ) { + case 0: *i1 = reg_num; break; + case 1: *i2 = reg_num; break; + case 2: *i3 = reg_num; break; + default: + break; + } + m++; + } else if ( o.is_vector() ) { + assert(m == 0); //only support 1 vector operand (for textures) right now + *vectorout = 1; + *i1 = o.reg1_num(); + *i2 = o.reg2_num(); + *i3 = o.reg3_num(); + *i4 = o.reg4_num(); + for (int i = 0; i < 4; i++) + arch_reg[i + 4] = o.arch_reg_num(i); + m+=4; + } + } + } +} + +unsigned function_info::ptx_get_inst_op( ptx_thread_info *thread ) +{ + addr_t pc = thread->get_pc(); + unsigned index = pc - m_start_PC; + assert( index < m_instr_mem_size ); + ptx_instruction *pI = m_instr_mem[index]; //get instruction from m_instr_mem[PC] + + int opcode = pI->get_opcode(); //determine the opcode + + if ( opcode == LD_OP ) { + if ( pI->get_space() != SHARED_DIRECTIVE ) //treat shared memory access as generic instruction (e.g. no bank conflicts) + return LOAD_OP; + } else if ( opcode == ST_OP ) { + if ( pI->get_space() != SHARED_DIRECTIVE ) + return STORE_OP; + } else if ( opcode == BRA_OP ) { + return BRANCH_OP; + } else if ( opcode == TEX_OP ) { + return LOAD_OP; + } + return ALU_OP; +} + +void function_info::add_param_name_and_type( unsigned index, std::string name, int type ) +{ + unsigned parsed_index; + char buffer[2048]; + snprintf(buffer,2048,"%s_param_%%u", m_name.c_str() ); + int ntokens = sscanf(name.c_str(),buffer,&parsed_index); + if( ntokens == 1 ) { + assert( m_ptx_param_info.find(parsed_index) == m_ptx_param_info.end() ); + m_ptx_param_info[parsed_index] = param_info(parsed_index,name,type); + } else { + assert( m_ptx_param_info.find(index) == m_ptx_param_info.end() ); + m_ptx_param_info[index] = param_info(index,name,type); + } +} + +void function_info::add_param_data( unsigned argn, struct gpgpu_ptx_sim_arg *args ) +{ + const void *data = args->m_start; + param_t tmp; + + if( data ) { + memcpy(&tmp.data,data,args->m_nbytes); + std::map<unsigned,param_info>::iterator i=m_ptx_param_info.find(argn); + if( i != m_ptx_param_info.end()) { + i->second.add_data(tmp); + } else { + // This should only happen for OpenCL: + // + // The LLVM PTX compiler in NVIDIA's driver (version 190.29) + // does not generate an argument in the function declaration + // for __constant arguments. + // + // The associated constant memory space can be allocated in two + // ways. It can be explicitly initialized in the .ptx file where + // it is declared. Or, it can be allocated using the clCreateBuffer + // on the host. In this later case, the .ptx file will contain + // a global declaration of the parameter, but it will have an unknown + // array size. Thus, the symbol's address will not be set and we need + // to set it here before executing the PTX. + + char buffer[2048]; + snprintf(buffer,2048,"%s_param_%u",m_name.c_str(),argn); + + symbol *p = m_symtab->lookup(buffer); + if( p == NULL ) + printf("GPGPU-Sim PTX: ERROR ** could not locate symbol for \'%s\' : cannot bind buffer\n", buffer); + p->set_address((addr_t)*(size_t*)data); + } + } else { + // This should only happen for OpenCL, but doesn't cause problems + } +} + +int ptx_branch_taken( void *thd ) +{ + ptx_thread_info *thread = (ptx_thread_info *) thd; + return (thread != NULL) && thread->branch_taken(); +} + +template<int activate_level> +bool ptx_debug_exec_dump_cond(int thd_uid, addr_t pc) +{ + if (g_debug_execution >= activate_level) { + // check each type of debug dump constraint to filter out dumps + if ( (g_debug_thread_uid != 0) && (thd_uid != (unsigned)g_debug_thread_uid) ) { + return false; + } + if ( (g_debug_pc != 0xBEEF1518) && (pc != g_debug_pc) ) { + return false; + } + + return true; + } + + return false; +} + +unsigned datatype2size( unsigned data_type ) +{ + unsigned data_size; + switch ( data_type ) { + case B8_TYPE: + case S8_TYPE: + case U8_TYPE: + data_size = 1; break; + case B16_TYPE: + case S16_TYPE: + case U16_TYPE: + case F16_TYPE: + data_size = 2; break; + case B32_TYPE: + case S32_TYPE: + case U32_TYPE: + case F32_TYPE: + data_size = 4; break; + case B64_TYPE: + case S64_TYPE: + case U64_TYPE: + case F64_TYPE: + data_size = 8; break; + default: assert(0); break; + } + return data_size; +} + +extern unsigned long long gpu_sim_cycle; +unsigned g_warp_active_mask; + +void function_info::ptx_exec_inst( ptx_thread_info *thread, + addr_t *addr, + unsigned *space, + unsigned *data_size, + dram_callback_t* callback, + unsigned warp_active_mask ) +{ + bool skip = false; + int op_classification = 0; + addr_t pc = thread->next_instr(); + unsigned index = pc - m_start_PC; + assert( index < m_instr_mem_size ); + ptx_instruction *pI = m_instr_mem[index]; + g_current_symbol_table = thread->get_finfo()->get_symtab(); + thread->clearRPC(); + thread->m_last_set_operand_value.u64 = 0; + *space = -1; + *addr = 0xFEEBDAED; + + thread->clear_modifiedregs(); + if( pI->has_pred() ) { + const operand_info &pred = pI->get_pred(); + ptx_reg_t pred_value = thread->get_operand_value(pred); + skip = !pred_value.pred ^ pI->get_pred_neg(); + } + g_warp_active_mask = warp_active_mask; + if( !skip ) { + switch ( pI->get_opcode() ) { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) case OP: FUNC(pI,thread); op_classification = CLASSIFICATION; break; +#include "opcodes.def" +#undef OP_DEF + default: + printf( "Execution error: Invalid opcode (0x%x)\n", pI->get_opcode() ); + break; + } + } + + if ( ptx_debug_exec_dump_cond<5>(thread->get_uid(), pc) ) { + dim3 ctaid = thread->get_ctaid(); + dim3 tid = thread->get_tid(); + printf("%u [cyc=%u][thd=%u][i=%u] : ctaid=(%u,%u,%u) tid=(%u,%u,%u) icount=%u [pc=%u] (%s:%u - %s) [0x%llx]\n", + g_ptx_sim_num_insn, + (unsigned)gpu_sim_cycle, + thread->get_uid(), + pI->uid(), ctaid.x,ctaid.y,ctaid.z,tid.x,tid.y,tid.z, + thread->get_icount(), + pc, pI->source_file(), pI->source_line(), pI->get_source(), + thread->m_last_set_operand_value.u64 ); + fflush(stdout); + } + + *data_size = 0; + if ( (pI->get_opcode() == LD_OP || pI->get_opcode() == ST_OP) ) { + *addr = thread->last_eaddr(); + *space = thread->last_space(); + + unsigned to_type = pI->get_type(); + *data_size = datatype2size(to_type); + } + + if ( pI->get_opcode() == ATOM_OP ) { + *addr = thread->last_eaddr(); + *space = thread->last_space(); + callback->function = thread->last_callback().function; + callback->instruction = thread->last_callback().instruction; + callback->thread = thread; + + unsigned to_type = pI->get_type(); + *data_size = datatype2size(to_type); + } else { + // make sure that the callback isn't set + callback->function = NULL; + callback->instruction = NULL; + } + + if (pI->get_opcode() == TEX_OP) { + *addr = thread->last_eaddr(); + *space = thread->last_space(); + + unsigned to_type = pI->get_type(); + *data_size = datatype2size(to_type); + } + + if ( g_debug_execution >= 6 ) { + if ( ptx_debug_exec_dump_cond<6>(thread->get_uid(), pc) ) + thread->dump_modifiedregs(); + } else if ( g_debug_execution >= 10 ) { + if ( ptx_debug_exec_dump_cond<10>(thread->get_uid(), pc) ) + thread->dump_regs(); + } + thread->update_pc(); + g_ptx_sim_num_insn++; + ptx_file_line_stats_add_exec_count(pI); + if ( gpgpu_ptx_instruction_classification ) { + unsigned space = pI->get_space(); + switch ( space ) { + case GLOBAL_DIRECTIVE: space = 10; break; + case LOCAL_DIRECTIVE: space = 11; break; + case TEX_DIRECTIVE: space = 12; break; + case SURF_DIRECTIVE: space = 13; break; + case PARAM_DIRECTIVE: space = 14; break; + case SHARED_DIRECTIVE: space = 15; break; + case CONST_DIRECTIVE: space = 16; break; + default: + space = 0 ; + break; + } + StatAddSample( g_inst_classification_stat[g_ptx_kernel_count], op_classification); + if (space) StatAddSample( g_inst_classification_stat[g_ptx_kernel_count], ( int )space); + StatAddSample( g_inst_op_classification_stat[g_ptx_kernel_count], (int) pI->get_opcode() ); + } + if ( (g_ptx_sim_num_insn % 100000) == 0 ) { + dim3 ctaid = thread->get_ctaid(); + dim3 tid = thread->get_tid(); + printf("%u instructions simulated : ctaid=(%u,%u,%u) tid=(%u,%u,%u)\n", + g_ptx_sim_num_insn, ctaid.x,ctaid.y,ctaid.z,tid.x,tid.y,tid.z ); + fflush(stdout); + } +} + +unsigned g_gx, g_gy, g_gz; + +dim3 g_cudaGridDim, g_cudaBlockDim; + +unsigned g_cta_launch_sid; +std::list<ptx_thread_info *> g_active_threads; +std::map<unsigned,unsigned> g_sm_idx_offset_next; +unsigned g_sm_next_index; +std::map<unsigned,memory_space*> g_shared_memory_lookup; +std::map<unsigned,ptx_cta_info*> g_ptx_cta_lookup; +std::map<unsigned,std::map<unsigned,memory_space*> > g_local_memory_lookup; + +// return number of blocks in grid +unsigned ptx_sim_grid_size() +{ + return g_cudaGridDim.x * g_cudaGridDim.y * g_cudaGridDim.z; +} + +void set_option_gpgpu_spread_blocks_across_cores(int option) +{ + gpgpu_option_spread_blocks_across_cores = option; +} + +void set_param_gpgpu_num_shaders(int num_shaders) +{ + gpgpu_param_num_shaders = num_shaders; +} + +unsigned ptx_sim_cta_size() +{ + return g_cudaBlockDim.x * g_cudaBlockDim.y * g_cudaBlockDim.z; +} + +const struct gpgpu_ptx_sim_kernel_info* ptx_sim_kernel_info() { + return g_entrypoint_func_info->get_kernel_info(); +} + +void ptx_sim_free_sm( void** thread_info ) +{ +} + +unsigned ptx_sim_init_thread( void** thread_info,int sid,unsigned tid,unsigned threads_left,unsigned num_threads, core_t *core, unsigned hw_cta_id, unsigned hw_warp_id ) +{ + if ( *thread_info != NULL ) { + ptx_thread_info *thd = *((ptx_thread_info**)thread_info); + assert( thd->is_done() ); + if ( g_debug_execution==-1 ) { + dim3 ctaid = thd->get_ctaid(); + dim3 tid = thd->get_tid(); + printf("GPGPU-Sim PTX simulator: thread exiting ctaid=(%u,%u,%u) tid=(%u,%u,%u) @ 0x%Lx\n", + ctaid.x,ctaid.y,ctaid.z,tid.x,tid.y,tid.z, (unsigned long long)*((void**)thread_info) ); + fflush(stdout); + } + thd->m_cta_info->assert_barrier_empty(); + thd->m_cta_info->register_deleted_thread(thd); + delete thd; + *((ptx_thread_info**)thread_info) = NULL; + } + + if ( !g_active_threads.empty() ) { //if g_active_threads not empty... + assert( g_active_threads.size() <= threads_left ); + if ( g_cta_launch_sid == (unsigned)-1 ) + g_cta_launch_sid = sid; + assert( g_cta_launch_sid == (unsigned)sid ); + ptx_thread_info *thd = g_active_threads.front(); + g_active_threads.pop_front(); + *((ptx_thread_info**)thread_info) = thd; + thd->set_hw_tid(tid); + thd->set_hw_wid(hw_warp_id); + thd->set_hw_ctaid(hw_cta_id); + thd->set_core(core); + thd->set_hw_sid(sid); + return 1; + } + + if ( g_gx >= g_cudaGridDim.x || g_gy >= g_cudaGridDim.y || g_gz >= g_cudaGridDim.z ) { + return 0; //finished! + } + + if ( threads_left < ptx_sim_cta_size() ) { + return 0; + } + + if ( g_debug_execution==-1 ) { + printf("GPGPU-Sim PTX simulator: STARTING THREAD ALLOCATION --> \n"); + fflush(stdout); + } + + //initializing new CTA + ptx_cta_info *cta_info = NULL; + memory_space *shared_mem = NULL; + + unsigned cta_size = ptx_sim_cta_size(); //blocksize + unsigned sm_offset = g_sm_idx_offset_next[sid]; + unsigned max_cta_per_sm = num_threads/cta_size; // e.g., 256 / 48 = 5 + assert( max_cta_per_sm > 0 ); + + //following sm_idx calculation assumes shaders being filled up depth-first? + unsigned sm_idx = sid*max_cta_per_sm + sm_offset; /* Is this assuming non-even block distribution? */ + sm_idx = max_cta_per_sm*sid + tid/cta_size; + + + if (!gpgpu_option_spread_blocks_across_cores) { + // update offset... + if ( (sm_offset + 1) >= max_cta_per_sm ) { + sm_offset = 0; + } else { + sm_offset++; + } + g_sm_idx_offset_next[sid] = sm_offset; + } else { + sm_idx = (tid/cta_size)*gpgpu_param_num_shaders + sid; + } + + if ( g_shared_memory_lookup.find(sm_idx) == g_shared_memory_lookup.end() ) { + if ( g_debug_execution >= 1 ) { + printf(" <CTA alloc> : sm_idx=%u sid=%u sm_offset=%u max_cta_per_sm=%u\n", + sm_idx, sid, sm_offset, max_cta_per_sm ); + } + char buf[512]; + snprintf(buf,512,"shared_%u", sid); + shared_mem = new memory_space_impl<16*1024>(buf,4); + g_shared_memory_lookup[sm_idx] = shared_mem; + cta_info = new ptx_cta_info(sm_idx); + g_ptx_cta_lookup[sm_idx] = cta_info; + } else { + if ( g_debug_execution >= 1 ) { + printf(" <CTA realloc> : sm_idx=%u sid=%u sm_offset=%u max_cta_per_sm=%u\n", + sm_idx, sid, sm_offset, max_cta_per_sm ); + } + shared_mem = g_shared_memory_lookup[sm_idx]; + cta_info = g_ptx_cta_lookup[sm_idx]; + cta_info->check_cta_thread_status_and_reset(); + } + + std::map<unsigned,memory_space*> &local_mem_lookup = g_local_memory_lookup[sid]; + unsigned new_tid; + for ( unsigned tz=0; tz < g_cudaBlockDim.z; tz++ ) { + for ( unsigned ty=0; ty < g_cudaBlockDim.y; ty++ ) { + for ( unsigned tx=0; tx < g_cudaBlockDim.x; tx++ ) { + new_tid = tx + g_cudaBlockDim.x*ty + g_cudaBlockDim.x*g_cudaBlockDim.y*tz; //unique id but only to a BLOCK + new_tid += tid; + ptx_thread_info *thd = new ptx_thread_info(); + + memory_space *local_mem = NULL; + std::map<unsigned,memory_space*>::iterator l = local_mem_lookup.find(new_tid); + if ( l != local_mem_lookup.end() ) { + local_mem = l->second; + } else { + char buf[512]; + snprintf(buf,512,"local_%u_%u", sid, new_tid); + local_mem = new memory_space_impl<32>(buf,32); + local_mem_lookup[new_tid] = local_mem; + } + thd->set_info(g_entrypoint_symbol_table,g_entrypoint_func_info); + thd->set_nctaid(g_cudaGridDim.x,g_cudaGridDim.y,g_cudaGridDim.z); + thd->set_ntid(g_cudaBlockDim.x,g_cudaBlockDim.y,g_cudaBlockDim.z); + thd->set_ctaid(g_gx,g_gy,g_gz); + thd->set_tid(tx,ty,tz); + thd->set_hw_tid((unsigned)-1); + thd->set_hw_wid((unsigned)-1); + thd->set_hw_ctaid((unsigned)-1); + thd->set_core(NULL); + thd->set_hw_sid((unsigned)-1); + thd->set_valid(); + thd->m_shared_mem = shared_mem; + thd->m_cta_info = cta_info; + cta_info->add_thread(thd); + thd->m_local_mem = local_mem; + if ( g_debug_execution==-1 ) { + printf("GPGPU-Sim PTX simulator: allocating thread ctaid=(%u,%u,%u) tid=(%u,%u,%u) @ 0x%Lx\n", + g_gx,g_gy,g_gz,tx,ty,tz, (unsigned long long)thd ); + fflush(stdout); + } + g_active_threads.push_back(thd); + } + } + } + if ( g_debug_execution==-1 ) { + printf("GPGPU-Sim PTX simulator: <-- FINISHING THREAD ALLOCATION\n"); + fflush(stdout); + } + + g_gx++; + if ( g_gx >= g_cudaGridDim.x ) { + g_gx = 0; + g_gy++; + if ( g_gy >= g_cudaGridDim.y ) { + g_gy = 0; + g_gz++; + } + } + + g_cta_launch_sid = -1; + + assert( g_active_threads.size() <= threads_left ); + + g_cta_launch_sid = sid; + *((ptx_thread_info**)thread_info) = g_active_threads.front(); + (*((ptx_thread_info**)thread_info) )->set_hw_tid(tid); + (*((ptx_thread_info**)thread_info) )->set_hw_wid(hw_warp_id); + (*((ptx_thread_info**)thread_info) )->set_hw_ctaid(hw_cta_id); + (*((ptx_thread_info**)thread_info) )->set_core(core); + (*((ptx_thread_info**)thread_info) )->set_hw_sid(sid); + g_active_threads.pop_front(); + + return 1; +} + +void init_inst_classification_stat() { + char kernelname[256] =""; +#define MAX_CLASS_KER 256 + if (!g_inst_classification_stat) g_inst_classification_stat = (void**)calloc(MAX_CLASS_KER, sizeof(void*)); + snprintf(kernelname, MAX_CLASS_KER, "Kernel %d Classification\n",g_ptx_kernel_count ); + assert( g_ptx_kernel_count < MAX_CLASS_KER ) ; // a static limit on number of kernels increase it if it fails! + g_inst_classification_stat[g_ptx_kernel_count] = StatCreate(kernelname,1,20); + if (!g_inst_op_classification_stat) g_inst_op_classification_stat = (void**)calloc(MAX_CLASS_KER, sizeof(void*)); + snprintf(kernelname, MAX_CLASS_KER, "Kernel %d OP Classification\n",g_ptx_kernel_count ); + g_inst_op_classification_stat[g_ptx_kernel_count] = StatCreate(kernelname,1,100); +} + +unsigned g_max_regs_per_thread = 0; + +std::map<std::string,function_info*> *g_kernel_name_to_function_lookup=NULL; +std::map<std::string,symbol_table*> g_kernel_name_to_symtab_lookup; +std::map<const void*,std::string> *g_host_to_kernel_entrypoint_name_lookup=NULL; +extern unsigned g_ptx_thread_info_uid_next; + +void gpgpu_ptx_sim_init_grid( const char *kernel_key, struct gpgpu_ptx_sim_arg* args, + struct dim3 gridDim, struct dim3 blockDim ) +{ + g_gx=0; + g_gy=0; + g_gz=0; + g_cudaGridDim = gridDim; + g_cudaBlockDim = blockDim; + g_sm_idx_offset_next.clear(); + g_sm_next_index = 0; + + if ( g_host_to_kernel_entrypoint_name_lookup->find(kernel_key) == + g_host_to_kernel_entrypoint_name_lookup->end() ) { + printf("GPGPU-Sim PTX: ERROR ** cannot locate PTX entry point\n" ); + abort(); + } else { + std::string kname = (*g_host_to_kernel_entrypoint_name_lookup)[kernel_key]; + printf("GPGPU-Sim PTX: Launching kernel \'%s\' gridDim= (%u,%u,%u) blockDim = (%u,%u,%u); ntuid=%u\n", + kname.c_str(), g_cudaGridDim.x,g_cudaGridDim.y,g_cudaGridDim.z,g_cudaBlockDim.x,g_cudaBlockDim.y,g_cudaBlockDim.z, + g_ptx_thread_info_uid_next ); + + if ( g_kernel_name_to_function_lookup->find(kname) == + g_kernel_name_to_function_lookup->end() ) { + printf("GPGPU-Sim PTX: ERROR ** function \'%s\' not found in ptx file\n", kname.c_str() ); + abort(); + } + g_entrypoint_func_info = g_func_info = (*g_kernel_name_to_function_lookup)[kname]; + g_entrypoint_symbol_table = g_current_symbol_table = g_kernel_name_to_symtab_lookup[kname]; + } + + unsigned argcount=0; + struct gpgpu_ptx_sim_arg *tmparg = args; + while (tmparg) { + tmparg = tmparg->m_next; + argcount++; + } + + unsigned argn=1; + while (args) { + g_func_info->add_param_data(argcount-argn,args); + args = args->m_next; + argn++; + } + g_func_info->finalize(g_param_mem,g_current_symbol_table); + g_ptx_kernel_count++; + if ( gpgpu_ptx_instruction_classification ) { + init_inst_classification_stat(); + } + fflush(stdout); +} + +const char *g_gpgpusim_version_string = "2.1.1b (beta)"; + +void print_splash() +{ + static int splash_printed=0; + if ( !splash_printed ) { + fprintf(stdout, "\n\n *** GPGPU-Sim version %s ***\n\n\n", g_gpgpusim_version_string ); + splash_printed=1; + } +} + +void gpgpu_ptx_sim_register_kernel(const char *hostFun, const char *deviceFun) +{ + const void* key=hostFun; + print_splash(); + if ( g_host_to_kernel_entrypoint_name_lookup == NULL ) + g_host_to_kernel_entrypoint_name_lookup = new std::map<const void*,std::string>; + if( g_kernel_name_to_function_lookup == NULL ) + g_kernel_name_to_function_lookup = new std::map<std::string,function_info*>; + if ( g_host_to_kernel_entrypoint_name_lookup->find(key) != + g_host_to_kernel_entrypoint_name_lookup->end() ) { + printf("GPGPU-Sim Loader error: Don't know how to identify PTX kernels during cudaLaunch\n" + " for this application.\n"); + abort(); + } + (*g_host_to_kernel_entrypoint_name_lookup)[key] = deviceFun; + if( g_kernel_name_to_function_lookup->find(deviceFun) == + g_kernel_name_to_function_lookup->end() ) { + (*g_kernel_name_to_function_lookup)[deviceFun] = NULL; // we set this later, set keys now for error checking + } + + printf("GPGPU-Sim PTX: __cudaRegisterFunction %s : 0x%Lx\n", deviceFun, (unsigned long long)hostFun); +} + +extern int ptx_lineno; + +void register_ptx_function( const char *name, function_info *impl, symbol_table *symtab ) +{ + printf("GPGPU-Sim PTX: parsing function %s\n", name ); + if( g_kernel_name_to_function_lookup == NULL ) + g_kernel_name_to_function_lookup = new std::map<std::string,function_info*>; + + std::map<std::string,function_info*>::iterator i_kernel = g_kernel_name_to_function_lookup->find(name); + if (i_kernel != g_kernel_name_to_function_lookup->end() && i_kernel->second != NULL) { + printf("GPGPU-Sim PTX: WARNING: Function already parsed once. Overwriting.\n"); + } + (*g_kernel_name_to_function_lookup)[name] = impl; + g_kernel_name_to_symtab_lookup[name] = symtab; +} + +std::map<const void*,std::string> g_const_name_lookup; // indexed by hostVar +std::map<const void*,std::string> g_global_name_lookup; // indexed by hostVar +std::set<std::string> g_globals; +std::set<std::string> g_constants; +extern std::map<std::string,symbol_table*> g_sym_name_to_symbol_table; + +void gpgpu_ptx_sim_register_const_variable(void *hostVar, const char *deviceName, size_t size ) +{ + printf("GPGPU-Sim PTX registering constant %s (%zu bytes) to name mapping\n", deviceName, size ); + g_const_name_lookup[hostVar] = deviceName; + //assert( g_current_symbol_table != NULL ); + //g_sym_name_to_symbol_table[deviceName] = g_current_symbol_table; +} + +void gpgpu_ptx_sim_register_global_variable(void *hostVar, const char *deviceName, size_t size ) +{ + printf("GPGPU-Sim PTX registering global %s hostVar to name mapping\n", deviceName ); + g_global_name_lookup[hostVar] = deviceName; + //assert( g_current_symbol_table != NULL ); + //g_sym_name_to_symbol_table[deviceName] = g_current_symbol_table; +} + +void gpgpu_ptx_sim_memcpy_symbol(const char *hostVar, const void *src, size_t count, size_t offset, int to ) +{ + printf("GPGPU-Sim PTX: starting gpgpu_ptx_sim_memcpy_symbol with hostVar 0x%p\n", hostVar); + bool found_sym = false; + int mem_region = 0; + std::string sym_name; + + std::map<const void*,std::string>::iterator c=g_const_name_lookup.find(hostVar); + if ( c!=g_const_name_lookup.end() ) { + found_sym = true; + sym_name = c->second; + mem_region = CONST_DIRECTIVE; + } + std::map<const void*,std::string>::iterator g=g_global_name_lookup.find(hostVar); + if ( g!=g_global_name_lookup.end() ) { + if ( found_sym ) { + printf("Execution error: PTX symbol \"%s\" w/ hostVar=0x%Lx is declared both const and global?\n", + sym_name.c_str(), (unsigned long long)hostVar ); + abort(); + } + found_sym = true; + sym_name = g->second; + mem_region = GLOBAL_DIRECTIVE; + } + if( g_globals.find(hostVar) != g_globals.end() ) { + found_sym = true; + sym_name = hostVar; + mem_region = GLOBAL_DIRECTIVE; + } + if( g_constants.find(hostVar) != g_constants.end() ) { + found_sym = true; + sym_name = hostVar; + mem_region = CONST_DIRECTIVE; + } + + if ( !found_sym ) { + printf("Execution error: No information for PTX symbol w/ hostVar=0x%Lx\n", (unsigned long long)hostVar ); + abort(); + } else printf("GPGPU-Sim PTX: gpgpu_ptx_sim_memcpy_symbol: Found PTX symbol w/ hostVar=0x%Lx\n", (unsigned long long)hostVar ); + const char *mem_name = NULL; + memory_space *mem = NULL; + + std::map<std::string,symbol_table*>::iterator st = g_sym_name_to_symbol_table.find(sym_name.c_str()); + if( st != g_sym_name_to_symbol_table.end() ) { + g_current_symbol_table = st->second; + } + + symbol *sym = g_current_symbol_table->lookup(sym_name.c_str()); + assert(sym); + unsigned dst = sym->get_address() + offset; + switch (mem_region) { + case CONST_DIRECTIVE: + mem = g_global_mem; + mem_name = "global"; + break; + case GLOBAL_DIRECTIVE: + mem = g_global_mem; + mem_name = "global"; + break; + default: + abort(); + } + printf("GPGPU-Sim PTX: gpgpu_ptx_sim_memcpy_symbol: copying %zu bytes %s symbol %s+%zu @0x%x ...\n", + count, (to?" to ":"from"), sym_name.c_str(), offset, dst ); + for ( unsigned n=0; n < count; n++ ) { + if( to ) mem->write(dst+n,1,((char*)src)+n); + else mem->read(dst+n,1,((char*)src)+n); + } + fflush(stdout); +} + +int g_ptx_sim_mode=0; +// used by libcuda.a if non-zer cudaLaunch() will call gpgpu_ptx_sim_main_func() +// if zero it calls gpgpu_ptx_sim_main_perf() + +#if defined(__APPLE__) +int ptx_file_filter(struct dirent *de ) +#else +int ptx_file_filter(const struct dirent *de ) +#endif +{ + const char *tmp = strstr(de->d_name,".ptx"); + if ( tmp != NULL && tmp[4] == 0 ) { + return 1; + } + return 0; +} + +void read_environment_variables() +{ + ptx_debug = 0; + g_debug_execution = 0; + g_debug_ir_generation = false; + + char *mode = getenv("PTX_SIM_MODE_FUNC"); + if ( mode ) + sscanf(mode,"%u", &g_ptx_sim_mode); + printf("GPGPU-Sim PTX: simulation mode %d (can change with PTX_SIM_MODE_FUNC environment variable:\n", g_ptx_sim_mode); + printf(" 1=functional simulation only, 0=detailed performance simulator)\n"); + g_filename = getenv("PTX_SIM_KERNELFILE"); + char *dbg_level = getenv("PTX_SIM_DEBUG"); + if ( dbg_level && strlen(dbg_level) ) { + printf("GPGPU-Sim PTX: setting debug level to %s\n", dbg_level ); + fflush(stdout); + sscanf(dbg_level,"%d", &g_debug_execution); + } + char *dbg_thread = getenv("PTX_SIM_DEBUG_THREAD_UID"); + if ( dbg_thread && strlen(dbg_thread) ) { + printf("GPGPU-Sim PTX: printing debug information for thread uid %s\n", dbg_thread ); + fflush(stdout); + sscanf(dbg_thread,"%d", &g_debug_thread_uid); + } + char *dbg_pc = getenv("PTX_SIM_DEBUG_PC"); + if ( dbg_pc && strlen(dbg_pc) ) { + printf("GPGPU-Sim PTX: printing debug information for instruction with PC = %s\n", dbg_pc ); + fflush(stdout); + sscanf(dbg_pc,"%d", &g_debug_pc); + } + +#if CUDART_VERSION > 1010 + g_override_embedded_ptx = false; + char *usefile = getenv("PTX_SIM_USE_PTX_FILE"); + if (usefile && strlen(usefile)) { + printf("GPGPU-Sim PTX: overriding embedded ptx with ptx file (PTX_SIM_USE_PTX_FILE is set)\n"); + fflush(stdout); + g_override_embedded_ptx = true; + } +#else + g_override_embedded_ptx = true; +#endif + + if ( g_debug_execution >= 40 ) { + ptx_debug = 1; + } + if ( g_debug_execution >= 30 ) { + g_debug_ir_generation = true; + } +} + +static FILE *open_ptxinfo (const char* ptx_filename) +{ + const int ptx_fnamelen = strlen(ptx_filename); + char *ptxi_fname = new char[ptx_fnamelen+5]; + strcpy (ptxi_fname, ptx_filename); + strcpy (ptxi_fname+ptx_fnamelen, "info"); + + //ptxinfo_debug=1; + g_ptxinfo_filename = ptxi_fname; + FILE *f = fopen (ptxi_fname, "rt"); + return f; +} + +struct ptx_info_t { + char *str; + ptx_info_t *next; +}; + +ptx_info_t *g_ptx_source_array = NULL; +unsigned g_used_embedded_ptx_files; +extern double g_ptx_version; + +void gpgpu_ptx_sim_add_ptxstring( const char *ptx_string ) +{ + ptx_info_t *t = new ptx_info_t; + t->next = NULL; + t->str = strdup(ptx_string); + + // put ptx source into a fifo + if (g_ptx_source_array == NULL) { + // first ptx source + g_ptx_source_array = t; + } else { + // insert subsequent ptx source at the end of queue + ptx_info_t *l_ptx_source = g_ptx_source_array; + while (l_ptx_source->next != NULL) { + l_ptx_source = l_ptx_source->next; + } + l_ptx_source->next = t; + } +} + +const ptx_instruction *ptx_instruction_lookup( const char *filename, unsigned linenumber ); + +void print_ptx_file( const char *p, unsigned source_num, const char *filename ) +{ + printf("\nGPGPU-Sim PTX: file _%u.ptx contents:\n\n", source_num ); + char *s = strdup(p); + char *t = s; + unsigned n=1; + while ( *t != '\0' ) { + char *u = t; + while ( (*u != '\n') && (*u != '\0') ) u++; + unsigned last = (*u == '\0'); + *u = '\0'; + const ptx_instruction *pI = ptx_instruction_lookup(filename,n); + char pc[64]; + if( pI && pI->get_PC() ) + snprintf(pc,64,"%4u", pI->get_PC() ); + else + snprintf(pc,64," "); + printf(" _%u.ptx %4u (pc=%s): %s\n", source_num, n, pc, t ); + if ( last ) break; + t = u+1; + n++; + } + free(s); + fflush(stdout); +} + +extern int g_save_embedded_ptx; + +void gpgpu_ptx_sim_load_ptx_from_string( const char *p, unsigned source_num ) +{ + char buf[1024]; + snprintf(buf,1024,"_%u.ptx", source_num ); + if( g_save_embedded_ptx ) { + FILE *fp = fopen(buf,"w"); + fprintf(fp,"%s",p); + fclose(fp); + } + g_filename = strdup(buf); + init_parser(); + ptx__scan_string(p); + int errors = ptx_parse (); + if ( errors ) { + char fname[1024]; + snprintf(fname,1024,"_ptx_XXXXXX"); + int fd=mkstemp(fname); + close(fd); + printf("GPGPU-Sim PTX: parser error detected, exiting... but first extracting .ptx to \"%s\"\n", fname); + FILE *ptxfile = fopen(fname,"w"); + fprintf(ptxfile,"%s", p ); + fclose(ptxfile); + abort(); + exit(40); + } + + if ( g_debug_execution >= 1 ) + print_ptx_file(p,source_num,g_filename); + + printf("GPGPU-Sim PTX: finished parsing EMBEDDED .ptx file %s\n",g_filename); + + char fname[1024]; + snprintf(fname,1024,"_ptx_XXXXXX"); + int fd=mkstemp(fname); + close(fd); + + printf("GPGPU-Sim PTX: extracting embedded .ptx to temporary file \"%s\"\n", fname); + FILE *ptxfile = fopen(fname,"w"); + fprintf(ptxfile,"%s",p); + fclose(ptxfile); + + char fname2[1024]; + snprintf(fname2,1024,"_ptx2_XXXXXX"); + fd=mkstemp(fname2); + close(fd); + char commandline2[4096]; + snprintf(commandline2,4096,"cat %s | sed 's/.version 1.5/.version 1.4/' | sed 's/, texmode_independent//' | sed 's/\\(\\.extern \\.const\\[1\\] .b8 \\w\\+\\)\\[\\]/\\1\\[1\\]/' | sed 's/const\\[.\\]/const\\[0\\]/g' > %s", fname, fname2); + int result = system(commandline2); + if( result != 0 ) { + printf("GPGPU-Sim PTX: ERROR ** while loading PTX (a) %d\n", result); + printf(" Ensure you have write access to simulation directory\n"); + printf(" and have \'cat\' and \'sed\' in your path.\n"); + exit(1); + } + + char tempfile_ptxinfo[1024]; + snprintf(tempfile_ptxinfo,1024,"%sinfo",fname); + char commandline[1024]; + snprintf(commandline,1024,"ptxas -v %s --output-file /dev/null 2> %s", fname2, tempfile_ptxinfo); + printf("GPGPU-Sim PTX: generating ptxinfo using \"%s\"\n", commandline); + result = system(commandline); + if( result != 0 ) { + printf("GPGPU-Sim PTX: ERROR ** while loading PTX (b) %d\n", result); + printf(" Ensure ptxas is in your path.\n"); + exit(1); + } + + ptxinfo_in = fopen(tempfile_ptxinfo,"r"); + g_ptxinfo_filename = tempfile_ptxinfo; + ptxinfo_parse(); + snprintf(commandline,1024,"rm -f %s %s %s", fname, fname2, tempfile_ptxinfo); + printf("GPGPU-Sim PTX: removing ptxinfo using \"%s\"\n", commandline); + result = system(commandline); + if( result != 0 ) { + printf("GPGPU-Sim PTX: ERROR ** while loading PTX (c) %d\n", result); + exit(1); + } + g_filename = NULL; +} + +void gpgpu_ptx_assemble( std::string kname, void *kinfo ) +{ + function_info *func_info = (function_info *)kinfo; + g_func_info = func_info; + g_current_symbol_table = g_kernel_name_to_symtab_lookup[ kname ]; + if( g_current_symbol_table == NULL ) { + printf("\nGPGPU-Sim PTX: ERROR no information for kernel \'%s\'\n" + " this can happen for kernels contained in CUDA\n" + " libraries (such as CUBLAS, CUFFT, or CUDPP)\n", kname.c_str() ); + exit(1); + } + + func_info->ptx_assemble(); +} + +void gpgpu_ptx_sim_load_gpu_kernels() +{ + ptx_in = NULL; + if ( g_filename ) + ptx_in = fopen( g_filename, "r" ); + static unsigned source_num = 0; + gpgpu_ptx_sim_init_memory(); + if (ptx_in) { + init_parser(); + ptx_parse(); + ptxinfo_in = open_ptxinfo(g_filename); + ptxinfo_parse(); + load_static_globals(g_global_symbol_table,0x10000000,0xFFFFFFFF); + load_constants(g_global_symbol_table); + } else { + if (!g_override_embedded_ptx) { + g_used_embedded_ptx_files=1; + printf("GPGPU-Sim PTX: USING EMBEDDED .ptx files...\n"); + ptx_info_t *s; + for ( s=g_ptx_source_array; s!=NULL; s=s->next ) { + gpgpu_ptx_sim_load_ptx_from_string(s->str, ++source_num); + load_static_globals(g_global_symbol_table,0x10000000,0xFFFFFFFF); + load_constants(g_global_symbol_table); + } + } else { + g_filename = NULL; + struct dirent **namelist; + int n; + + n = scandir(".", &namelist, ptx_file_filter, alphasort); + if (n < 0) + perror("scandir"); + else { + while (n--) { + if ( g_filename != NULL ) { + printf("Loader error: support for multiple .ptx files not yet enabled\n"); + abort(); + } + g_filename = strdup(namelist[n]->d_name); + printf("Parsing %s..\n", g_filename); + ptx_in = fopen( g_filename, "r" ); + free(namelist[n]); + init_parser(); + ptx_parse (); + ptxinfo_in = open_ptxinfo(g_filename); + ptxinfo_parse(); + g_filename = NULL; + load_static_globals(g_global_symbol_table,0x10000000,0xFFFFFFFF); + load_constants(g_global_symbol_table); + } + free(namelist); + } + } + } + + if ( ptx_in == NULL && g_override_embedded_ptx ) { + printf("GPGPU-Sim PTX Simulator error: Could find/open .ptx file for reading\n"); + printf(" This means there are no .ptx files in the current directory.\n"); + printf(" Either place a .ptx file in the current directory, or ensure\n" ); + printf(" the PTX_SIM_KERNELFILE environment variable points to .ptx file.\n"); + printf(" PTX_SIM_KERNELFILE=\"%s\"\n", g_filename ); + exit(1); + } + + if ( g_error_detected ) { + printf( "GPGPU-Sim PTX: Program parsing completed: Errors detected.\n" ); + exit(1); + } else { + printf( "GPGPU-Sim PTX: Program parsing completed (max %u registers used per thread).\n", g_max_regs_per_thread ); + } + + if ( g_kernel_name_to_function_lookup ) { + for ( std::map<std::string,function_info*>::iterator f=g_kernel_name_to_function_lookup->begin(); + f != g_kernel_name_to_function_lookup->end(); f++ ) { + gpgpu_ptx_assemble(f->first,f->second); + } + } +} + +extern time_t simulation_starttime; + +ptx_cta_info *g_func_cta_info = NULL; + +#define MAX(a,b) (((a)>(b))?(a):(b)) + +void gpgpu_ptx_sim_main_func( const char *kernel_key, dim3 gridDim, dim3 blockDim, struct gpgpu_ptx_sim_arg *args) +{ + printf("GPGPU-Sim: Performing Functional Simulation...\n"); + + printf("ERROR: Need to derived core_t for functional simulation, functional simulation no longer operational\n"); + // also: need PDOM stack, etc... for functional simulation + exit(1); + + time_t end_time, elapsed_time, days, hrs, minutes, sec; + int i1, i2, i3, i4, o1, o2, o3, o4; + int vectorin, vectorout; + + gpgpu_ptx_sim_init_grid(kernel_key, args,gridDim,blockDim); + + memory_space *shared_mem = new memory_space_impl<16*1024>("shared",4); + + std::map<unsigned,memory_space*> lm_lookup; + + if ( g_func_cta_info == NULL ) + g_func_cta_info = new ptx_cta_info(0); + + for ( unsigned gx=0; gx < gridDim.x; gx++ ) { + for ( unsigned gy=0; gy < gridDim.y; gy++ ) { + for ( unsigned gz=0; gz < gridDim.z; gz++ ) { + std::list<ptx_thread_info *> active_threads; + std::list<ptx_thread_info *> blocked_threads; + + g_func_cta_info->check_cta_thread_status_and_reset(); + + for ( unsigned tx=0; tx < blockDim.x; tx++ ) { + for ( unsigned ty=0; ty < blockDim.y; ty++ ) { + for ( unsigned tz=0; tz < blockDim.z; tz++ ) { + memory_space *local_mem = NULL; + ptx_thread_info *thd = new ptx_thread_info(); + + unsigned lm_idx = blockDim.x*blockDim.y*tz + blockDim.x * ty + tx; + std::map<unsigned,memory_space*>::iterator lm=lm_lookup.find(lm_idx); + if ( lm == lm_lookup.end() ) { + char buf[1024]; + snprintf(buf,1024,"local_(%u,%u,%u)", tx, ty, tz ); + local_mem = new memory_space_impl<32>(buf,32); + lm_lookup[lm_idx] = local_mem; + } else { + local_mem = lm->second; + } + + + thd->set_info(g_current_symbol_table,g_func_info); + thd->set_nctaid(gridDim.x,gridDim.y,gridDim.z); + thd->set_ntid(blockDim.x, blockDim.y, blockDim.z); + thd->set_ctaid(gx,gy,gz); + thd->set_tid(tx,ty,tz); + thd->set_valid(); + thd->m_shared_mem = shared_mem; + thd->m_local_mem = local_mem; + thd->m_cta_info = g_func_cta_info; + g_func_cta_info->add_thread(thd); + active_threads.push_back(thd); + } + } + } + + while ( !(active_threads.empty() && blocked_threads.empty()) ) { + // while there are still threads left to execute in this CTA + ptx_thread_info *thread = NULL; + + if ( !active_threads.empty() ) { + thread = active_threads.front(); + active_threads.pop_front(); + } else { + active_threads = blocked_threads; + blocked_threads.clear(); + std::list<ptx_thread_info *>::iterator a=active_threads.begin(); + for ( ; a != active_threads.end(); a++ ) { + ptx_thread_info *thd = *a; + thd->clear_barrier(); + } + g_func_cta_info->release_barrier(); + } + + while ( thread != NULL ) { + if ( thread->is_at_barrier() ) { + blocked_threads.push_back(thread); + thread = NULL; + break; + } + if ( thread->is_done() ) { + thread->m_cta_info->register_deleted_thread(thread); + delete thread; + thread = NULL; + break; + } + + unsigned op_type; + addr_t addr; + unsigned space; + int arch_reg[MAX_REG_OPERANDS] = { -1 }; + unsigned data_size; + dram_callback_t callback; + unsigned warp_active_mask = (unsigned)-1; // vote instruction with diverged warps won't execute correctly + // in functional simulation mode + + g_func_info->ptx_decode_inst( thread, &op_type, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg ); + g_func_info->ptx_exec_inst( thread, &addr, &space, &data_size, &callback, warp_active_mask ); + } + } + } + } + } + printf( "GPGPU-Sim: Done functional simulation (%u instructions simulated).\n", g_ptx_sim_num_insn ); + if ( gpgpu_ptx_instruction_classification ) { + StatDisp( g_inst_classification_stat[g_ptx_kernel_count]); + StatDisp ( g_inst_op_classification_stat[g_ptx_kernel_count]); + } + end_time = time((time_t *)NULL); + elapsed_time = MAX(end_time - simulation_starttime, 1); + + days = elapsed_time/(3600*24); + hrs = elapsed_time/3600 - 24*days; + minutes = elapsed_time/60 - 60*(hrs + 24*days); + sec = elapsed_time - 60*(minutes + 60*(hrs + 24*days)); + + fflush(stderr); + printf("\n\ngpgpu_simulation_time = %u days, %u hrs, %u min, %u sec (%u sec)\n", + (unsigned)days, (unsigned)hrs, (unsigned)minutes, (unsigned)sec, (unsigned)elapsed_time ); + printf("gpgpu_simulation_rate = %u (inst/sec)\n", (unsigned)(g_ptx_sim_num_insn / elapsed_time) ); + fflush(stdout); +} + +void ptx_decode_inst( void *thd, unsigned *op, int *i1, int *i2, int *i3, int *i4, int *o1, int *o2, int *o3, int *o4, int *vectorin, int *vectorout, int *arch_reg ) +{ + *op = NO_OP; + *o1 = 0; + *o2 = 0; + *o3 = 0; + *o4 = 0; + *i1 = 0; + *i2 = 0; + *i3 = 0; + *i4 = 0; + *vectorin = 0; + *vectorout = 0; + std::fill_n(arch_reg, MAX_REG_OPERANDS, -1); + + if ( thd == NULL ) + return; + + ptx_thread_info *thread = (ptx_thread_info *) thd; + g_func_info = thread->func_info(); + g_func_info->ptx_decode_inst(thread,op,i1,i2,i3,i4,o1,o2,o3,o4,vectorin,vectorout,arch_reg); +} + +unsigned ptx_get_inst_op( void *thd) +{ + if ( thd == NULL ) + return NO_OP; + + ptx_thread_info *thread = (ptx_thread_info *) thd; + return(thread->func_info())->ptx_get_inst_op(thread); +} + +void ptx_exec_inst( void *thd, address_type *addr, unsigned *space, unsigned *data_size, dram_callback_t* callback, unsigned warp_active_mask ) +{ + if ( thd == NULL ) + return; + ptx_thread_info *thread = (ptx_thread_info *) thd; + g_func_info = thread->func_info(); + g_func_info->ptx_exec_inst( thread, addr, space, data_size, callback, warp_active_mask ); +} + +void ptx_dump_regs( void *thd ) +{ + if ( thd == NULL ) + return; + ptx_thread_info *t = (ptx_thread_info *) thd; + t->dump_regs(); +} + +unsigned ptx_set_tex_cache_linesize(unsigned linesize) +{ + g_texcache_linesize = linesize; + return 0; +} + +unsigned ptx_kernel_program_size() +{ + return g_func_info->get_function_size(); +} + +unsigned translate_pc_to_ptxlineno(unsigned pc) +{ + // this function assumes that the kernel fits inside a single PTX file + // function_info *pFunc = g_func_info; // assume that the current kernel is the one in query + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + unsigned ptx_line_number = pInsn->source_line(); + + return ptx_line_number; +} + +int g_ptxinfo_error_detected; + + +static char *g_ptxinfo_kname = NULL; +static struct gpgpu_ptx_sim_kernel_info g_ptxinfo_kinfo; + +extern "C" void ptxinfo_function(const char *fname ) +{ + g_ptxinfo_kinfo.regs=0; + g_ptxinfo_kinfo.lmem=0; + g_ptxinfo_kinfo.smem=0; + g_ptxinfo_kinfo.cmem=0; + g_ptxinfo_kname = strdup(fname); +} + +extern "C" void ptxinfo_regs( unsigned nregs ) +{ + g_ptxinfo_kinfo.regs=nregs; +} + +extern "C" void ptxinfo_lmem( unsigned declared, unsigned system ) +{ + g_ptxinfo_kinfo.lmem=declared+system; +} + +extern "C" void ptxinfo_smem( unsigned declared, unsigned system ) +{ + g_ptxinfo_kinfo.smem=declared+system; +} + +extern "C" void ptxinfo_cmem( unsigned nbytes, unsigned bank ) +{ + g_ptxinfo_kinfo.cmem+=nbytes; +} + +extern "C" void ptxinfo_addinfo() +{ + if ( g_kernel_name_to_function_lookup ) { + std::map<std::string,function_info*>::iterator i=g_kernel_name_to_function_lookup->find(g_ptxinfo_kname); + if ( (g_kernel_name_to_function_lookup == NULL) || (i == g_kernel_name_to_function_lookup->end()) ) { + printf ("GPGPU-Sim PTX: Kernel '%s' in %s not found. Ignoring.\n", g_ptxinfo_kname, g_filename); + } else { + printf ("GPGPU-Sim PTX: Kernel %s\n", g_ptxinfo_kname); + function_info *fi = i->second; + fi->set_kernel_info(&g_ptxinfo_kinfo); + } + } else { + printf ("GPGPU-Sim PTX: Kernel '%s' in %s not found (no kernels registered).\n", g_ptxinfo_kname, g_filename); + } + + free(g_ptxinfo_kname); + g_ptxinfo_kname=NULL; + g_ptxinfo_kinfo.regs=0; + g_ptxinfo_kinfo.lmem=0; + g_ptxinfo_kinfo.smem=0; + g_ptxinfo_kinfo.cmem=0; +} + +void dwf_insert_reconv_pt(address_type pc); + +struct rec_pts { + gpgpu_recon_t *s_kernel_recon_points; + int s_num_recon; +}; + +struct std::map<function_info*,rec_pts> g_rpts; + +struct rec_pts find_reconvergence_points( function_info *finfo ) +{ + rec_pts tmp; + std::map<function_info*,rec_pts>::iterator r=g_rpts.find(finfo); + + if( r==g_rpts.end() ) { + int num_recon = finfo->get_num_reconvergence_pairs(); + + gpgpu_recon_t *kernel_recon_points = (struct gpgpu_recon_t*) calloc(num_recon, sizeof(struct gpgpu_recon_t)); + finfo->get_reconvergence_pairs(kernel_recon_points); + printf("Reconvergence Pairs for %s\n", finfo->get_name().c_str() ); + for (int i=0;i<num_recon;i++) + printf("%d\t%d\n", kernel_recon_points[i].source_pc, kernel_recon_points[i].target_pc); + tmp.s_kernel_recon_points = kernel_recon_points; + tmp.s_num_recon = num_recon; + g_rpts[finfo] = tmp; + } else { + tmp = r->second; + } + return tmp; +} + +unsigned int get_converge_point( unsigned int pc, void *thd ) +{ + // the branch could encode the reconvergence point and/or a bit that indicates the + // reconvergence point is the return PC on the call stack in the case the branch has + // no immediate postdominator in the function (i.e., due to multiple return points). + + std::map<unsigned,function_info*>::iterator f=g_pc_to_finfo.find(pc); + assert( f != g_pc_to_finfo.end() ); + function_info *finfo = f->second; + rec_pts tmp = find_reconvergence_points(finfo); + + int i=0; + for (; i < tmp.s_num_recon; ++i) { + if (tmp.s_kernel_recon_points[i].source_pc == pc) { + if( tmp.s_kernel_recon_points[i].target_pc == (unsigned) -2 ) { + // function call return + ptx_thread_info *the_thread = (ptx_thread_info*)thd; + assert( the_thread != NULL ); + return the_thread->get_return_PC(); + } else { + return tmp.s_kernel_recon_points[i].target_pc; + } + } + } + assert(i < tmp.s_num_recon); + abort(); // returning garbage! +} + +void find_reconvergence_points() +{ + find_reconvergence_points(g_func_info); +} + +void dwf_process_reconv_pts() +{ + rec_pts tmp = find_reconvergence_points(g_func_info); + for (int i = 0; i < tmp.s_num_recon; ++i) { + dwf_insert_reconv_pt(tmp.s_kernel_recon_points[i].target_pc); + } +} + +void *gpgpusim_opencl_getkernel_Object( const char *kernel_name ) +{ + std::map<std::string,function_info*>::iterator i=g_kernel_name_to_function_lookup->find(kernel_name); + + if( i == g_kernel_name_to_function_lookup->end() ) { + abort(); + return NULL; + } + return i->second; +} diff --git a/src/cuda-sim/dram_callback.h b/src/cuda-sim/dram_callback.h new file mode 100644 index 0000000..9336ee0 --- /dev/null +++ b/src/cuda-sim/dram_callback.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Miles Kaech, Dan O'Connor and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +////////////////////////////////////////////////////////// +// +// dram_callback.h +// by Miles Kaech +// +// defines a callback mechanism allowing instructions to be functionally executed +// once they arrive at dram. For example, atomic instructions should really be +// functionally simulated only once they hit the dram chips. +// +////////////////////////////////////////////////////////// + +#ifndef __DRAM_CALLBACK_H__ +#define __DRAM_CALLBACK_H__ + +typedef struct { + void (*function)(void* pI, void* gOldGThread); + void* instruction; + void* thread;// callback has to abuse g_thread when executed +}dram_callback_t; + +#endif // #ifndef __DRAM_CALLBACK_H__ diff --git a/src/cuda-sim/instructions.cc b/src/cuda-sim/instructions.cc new file mode 100644 index 0000000..a7a4df6 --- /dev/null +++ b/src/cuda-sim/instructions.cc @@ -0,0 +1,2921 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Ali Bakhoda, Joey Ting, Dan O'Connor, + * Clive Lin, George L. Yuan, Wilson W. L. Fung and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "instructions.h" +#include "ptx_ir.h" +#include "opcodes.h" +#include "ptx_sim.h" +#include "ptx.tab.h" +#include "dram_callback.h" +#include <stdlib.h> +#include <math.h> +#include <fenv.h> + +#include "cuda-math.h" +#include "../abstract_hardware_model.h" + +unsigned g_num_ptx_inst_uid=0; +unsigned cudasim_n_tex_insn=0; + +const char *g_opcode_string[NUM_OPCODES] = { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) STR, +#include "opcodes.def" +#undef OP_DEF +}; + +extern std::map<unsigned,std::string> g_ptx_token_decode; +extern std::map<struct textureReference*,struct cudaArray*> TextureToArrayMap; // texture bindings +extern std::map<struct textureReference*,struct textureInfo*> TextureToInfoMap; // texture bindings +extern std::map<std::string, struct textureReference*> NameToTextureMap; + +memory_space *g_global_mem; +memory_space *g_tex_mem; +memory_space *g_surf_mem; +memory_space *g_param_mem; + +void inst_not_implemented( const ptx_instruction * pI ) ; +unsigned unfound_register_warned = 0; + +ptx_reg_t ptx_thread_info::get_operand_value( const symbol *reg ) +{ + assert( reg->type()->get_key().is_reg() ); + const std::string &name = reg->name(); + std::map<std::string,ptx_reg_t>::iterator regs_iter = m_regs.back().find(name); + if (regs_iter == m_regs.back().end()) { + unsigned call_uid = m_callstack.back().m_call_uid; + ptx_reg_t uninit_reg; + uninit_reg.u32 = 0xDEADBEEF; + set_operand_value(reg, uninit_reg); // give it a value since we are going to warn the user anyway + std::string file_loc = get_location(); + if( !unfound_register_warned ) { + printf("GPGPU-Sim PTX: WARNING (%s) ** reading undefined register \'%s\' (cuid:%u). Setting to 0XDEADBEEF.\n", + file_loc.c_str(), name.c_str(), call_uid ); + unfound_register_warned = 1; + } + regs_iter = m_regs.back().insert(std::make_pair(name, uninit_reg)).first; + } + return regs_iter->second; +} + +ptx_reg_t ptx_thread_info::get_operand_value( const operand_info &op ) +{ + ptx_reg_t result, tmp; + const char *name = NULL; + if ( op.is_reg() ) { + result = get_operand_value( op.get_symbol() ); + } else if ( op.is_builtin()) { + result = get_builtin( op.get_int(), op.get_addr_offset() ); + } else if ( op.is_memory_operand() ) { + // a few options here... + const symbol *sym = op.get_symbol(); + const type_info *type = sym->type(); + const type_info_key &info = type->get_key(); + + if ( info.is_reg() ) { + name = op.name().c_str(); + std::map<std::string,ptx_reg_t>::iterator regs_iter = m_regs.back().find(name); + assert( regs_iter != m_regs.back().end() ); + tmp = regs_iter->second; + result.u64 = tmp.u64 + op.get_addr_offset(); + } else if ( info.is_param() ) { + result = sym->get_address() + op.get_addr_offset(); + } else if ( info.is_global() ) { + assert( op.get_addr_offset() == 0 ); + result = sym->get_address(); + } else if ( info.is_local() ) { + result = sym->get_address() + op.get_addr_offset(); + } else if ( info.is_const() ) { + result = sym->get_address() + op.get_addr_offset(); + } else if ( op.is_shared() ) { + result = op.get_symbol()->get_address() + op.get_addr_offset(); + } else { + assert(0); + } + + } else if ( op.is_literal() ) { + result = op.get_literal_value(); + } else if ( op.is_label() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_shared() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_const() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_global() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_local() ) { + result = op.get_symbol()->get_address(); + } else { + name = op.name().c_str(); + printf("ERROR! %s is neither local, global, const, shared, label, literal, memory operand, builtin, or register!\n",name); + assert(0); + } + + return result; +} + +unsigned get_operand_nbits( const operand_info &op ) +{ + if ( op.is_reg() ) { + const symbol *sym = op.get_symbol(); + const type_info *typ = sym->type(); + type_info_key t = typ->get_key(); + switch( t.scalar_type() ) { + case PRED_TYPE: + return 1; + case B8_TYPE: case S8_TYPE: case U8_TYPE: + return 8; + case S16_TYPE: case U16_TYPE: case F16_TYPE: case B16_TYPE: + return 16; + case S32_TYPE: case U32_TYPE: case F32_TYPE: case B32_TYPE: + return 32; + case S64_TYPE: case U64_TYPE: case F64_TYPE: case B64_TYPE: + return 64; + default: + printf("ERROR: unknown register type\n"); + fflush(stdout); + abort(); + } + } else { + printf("ERROR: Need to implement get_operand_nbits() for currently unsupported operand_info type\n"); + fflush(stdout); + abort(); + } + + return 0; +} + +void ptx_thread_info::get_vector_operand_values( const operand_info &op, ptx_reg_t* ptx_regs, unsigned num_elements ) +{ + const char *name1, *name2, *name3, *name4 = NULL; + if ( op.is_vector() ) { + if (num_elements > 3) { + name4 = op.vec_name4().c_str(); + assert( m_regs.back().find(name4) != m_regs.back().end() ); + ptx_regs[3] = m_regs.back()[ name4 ]; + } + if (num_elements > 2) { + name3 = op.vec_name3().c_str(); + assert( m_regs.back().find(name3) != m_regs.back().end() ); + ptx_regs[2] = m_regs.back()[ name3 ]; + } + name1 = op.vec_name1().c_str(); + name2 = op.vec_name2().c_str(); + assert( m_regs.back().find(name1) != m_regs.back().end() ); + assert( m_regs.back().find(name2) != m_regs.back().end() ); + ptx_regs[0] = m_regs.back()[ name1 ]; + ptx_regs[1] = m_regs.back()[ name2 ]; + } else { + assert(0); + } +} + +void sign_extend( ptx_reg_t &data, unsigned src_size, const operand_info &dst ) +{ + if( !dst.is_reg() ) + return; + unsigned dst_size = get_operand_nbits( dst ); + if( src_size >= dst_size ) + return; + // src_size < dst_size + unsigned long long mask = 1; + mask <<= (src_size-1); + if( (mask & data.u64) == 0 ) { + // no need to sign extend + return; + } + // need to sign extend + mask = 1; + mask <<= dst_size-src_size; + mask -= 1; + mask <<= src_size; + data.u64 |= mask; +} + +void ptx_thread_info::set_operand_value( const operand_info &dst, const ptx_reg_t &data ) +{ + m_regs.back()[ dst.name() ] = data; + m_debug_trace_regs_modified.back()[ dst.name() ] = data; + m_last_set_operand_value = data; +} + +void ptx_thread_info::set_operand_value( const symbol *dst, const ptx_reg_t &data ) +{ + m_regs.back()[ dst->name() ] = data; + m_debug_trace_regs_modified.back()[ dst->name() ] = data; + m_last_set_operand_value = data; +} + +void ptx_thread_info::set_vector_operand_values( const operand_info &dst, + const ptx_reg_t &data1, + const ptx_reg_t &data2, + const ptx_reg_t &data3, + const ptx_reg_t &data4, + unsigned num_elements ) +{ + m_regs.back()[ dst.vec_name1() ] = data1; + m_debug_trace_regs_modified.back()[ dst.vec_name1() ] = data1; + m_regs.back()[ dst.vec_name2() ] = data2; + m_debug_trace_regs_modified.back()[ dst.vec_name2() ] = data2; + if (num_elements > 2) { + m_regs.back()[ dst.vec_name3() ] = data3; + m_debug_trace_regs_modified.back()[ dst.vec_name3() ] = data3; + if (num_elements > 3) { + m_regs.back()[ dst.vec_name4() ] = data4; + m_debug_trace_regs_modified.back()[ dst.vec_name4() ] = data4; + } + } + m_last_set_operand_value = data1; +} + +#define my_abs(a) (((a)<0)?(-a):(a)) + +#define MY_MAX_I(a,b) (a > b) ? a : b +#define MY_MAX_F(a,b) isNaN(a) ? b : isNaN(b) ? a : (a > b) ? a : b + +#define MY_MIN_I(a,b) (a < b) ? a : b +#define MY_MIN_F(a,b) isNaN(a) ? b : isNaN(b) ? a : (a < b) ? a : b + +#define MY_INC_I(a,b) (a >= b) ? 0 : a+1 +#define MY_DEC_I(a,b) ((a == 0) || (a > b)) ? b : a-1 + +#define MY_CAS_I(a,b,c) (a == b) ? c : a + +#define MY_EXCH(a,b) b + +void abs_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case S16_TYPE: d.s16 = my_abs(a.s16); break; + case S32_TYPE: d.s32 = my_abs(a.s32); break; + case S64_TYPE: d.s64 = my_abs(a.s64); break; + case F32_TYPE: d.f32 = my_abs(a.f32); break; + case F64_TYPE: d.f64 = my_abs(a.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void add_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); //get operand info of sources and destination + const operand_info &src1 = pI->src1(); //use them to determine that they are of type 'register' + const operand_info &src2 = pI->src2(); + src1_data = thread->get_operand_value(src1); //get values from the operand infos + src2_data = thread->get_operand_value(src2); + + unsigned rounding_mode = pI->rounding_mode(); + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + + unsigned to_type = pI->get_type(); + + switch ( to_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + data.s64 = src1_data.s64 + src2_data.s64; break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = src1_data.f32 + src2_data.f32; break; + case F64_TYPE: data.f64 = src1_data.f64 + src2_data.f64; break; + default: assert(0); break; + } + fesetround( orig_rm ); + thread->set_operand_value(dst,data); +} + +void addc_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +void and_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 & src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void atom_callback( void* ptx_inst, void* thd ) +{ + ptx_thread_info *thread = (ptx_thread_info*)thd; + ptx_instruction *pI = (ptx_instruction*)ptx_inst; + + // Check state space + assert( pI->get_space()==GLOBAL_DIRECTIVE ); + + // "Decode" the output type + unsigned to_type = pI->get_type(); + size_t size; + int t; + type_decode(to_type, size, t); + + // Set up operand variables + ptx_reg_t data, // d + src1_data, // a + src2_data, // b + op_result; // temp variable to hold operation result + + bool data_ready = false; + + // Get operand info of sources and destination + const operand_info &dst = pI->dst(); // d + const operand_info &src1 = pI->src1(); // a + const operand_info &src2 = pI->src2(); // b + + // Get operand values + src1_data = thread->get_operand_value(src1); // a + src2_data = thread->get_operand_value(src2); // b + + // Copy value pointed to in operand 'a' into register 'd' + // (i.e. copy src1_data to dst) + g_global_mem->read(src1_data.u32,size/8,&data.s64); + thread->set_operand_value(dst, data); // Write value into register 'd' + + // Get the atomic operation to be performed + unsigned m_atomic_spec = pI->get_atomic(); + + switch ( m_atomic_spec ) { + // AND + case ATOMIC_AND: + { + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = data.u32 & src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 & src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.AND only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // OR + case ATOMIC_OR: + { + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = data.u32 | src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 | src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.OR only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // XOR + case ATOMIC_XOR: + { + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = data.u32 ^ src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 ^ src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.XOR only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // CAS + case ATOMIC_CAS: + { + + ptx_reg_t src3_data; + const operand_info &src3 = pI->src3(); + src3_data = thread->get_operand_value(src3); + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = MY_CAS_I(data.u32, src2_data.u32, src3_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_CAS_I(data.s32, src2_data.s32, src3_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.CAS only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // EXCH + case ATOMIC_EXCH: + { + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = MY_EXCH(data.u32, src2_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_EXCH(data.s32, src2_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.EXCH only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // ADD + case ATOMIC_ADD: + { + + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = data.u32 + src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 + src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.ADD only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // INC + case ATOMIC_INC: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_INC_I(data.u32, src2_data.u32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.INC only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // DEC + case ATOMIC_DEC: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_DEC_I(data.u32, src2_data.u32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.DEC only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // MIN + case ATOMIC_MIN: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_MIN_I(data.u32, src2_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_MIN_I(data.s32, src2_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.MIN only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // MAX + case ATOMIC_MAX: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_MAX_I(data.u32, src2_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_MAX_I(data.s32, src2_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.MAX only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // DEFAULT + default: + { + assert(0); + break; + } + } + + // Write operation result into global memory + // (i.e. copy src1_data to dst) + g_global_mem->write(src1_data.u32,size/8,&op_result.s64); +} + +// atom_impl will now result in a callback being called in mem_ctrl_pop (gpu-sim.c) +void atom_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + // SYNTAX + // atom.space.operation.type d, a, b[, c]; (now read in callback) + + // Check state space + assert( pI->get_space()==GLOBAL_DIRECTIVE ); + + // get the memory address + const operand_info &src1 = pI->src1(); + ptx_reg_t src1_data = thread->get_operand_value(src1); + + unsigned space = pI->get_space(); + + thread->m_last_effective_address = src1_data.u32; + thread->m_last_memory_space = space; + thread->m_last_dram_callback.function = atom_callback; + thread->m_last_dram_callback.instruction = (void*)pI; +} + +void bar_sync_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + ptx_reg_t b = thread->get_operand_value(dst); + assert( b.u32 == 0 ); // not clear what should happen if this is not zero +} + +void bra_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &target = pI->dst(); + ptx_reg_t target_pc = thread->get_operand_value(target); + + thread->m_branch_taken = true; + thread->set_npc(target_pc); +} + +void brkpt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +extern int gpgpu_simd_model; +#define POST_DOMINATOR 1 /* must match enum value in shader.h */ +void get_pdom_stack_top_info( unsigned sid, unsigned tid, unsigned *npc, unsigned *rpc ); + +void call_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + static unsigned call_uid_next = 1; + + const operand_info &target = pI->func_addr(); + assert( target.is_function_address() ); + const symbol *func_addr = target.get_symbol(); + const function_info *target_func = func_addr->get_pc(); + + // check that number of args and return match function requirements + if( pI->has_return() ^ target_func->has_return() ) { + printf("GPGPU-Sim PTX: Execution error - mismatch in number of return values between\n" + " call instruction and function declaration\n"); + abort(); + } + unsigned n_return = target_func->has_return(); + unsigned n_args = target_func->num_args(); + unsigned n_operands = pI->get_num_operands(); + + if( n_operands != (n_return+1+n_args) ) { + printf("GPGPU-Sim PTX: Execution error - mismatch in number of arguements between\n" + " call instruction and function declaration\n"); + abort(); + } + + // read source arguements into register specified in declaration of function + std::list< std::pair<const symbol*, ptx_reg_t> > arg_values; + for( unsigned arg=0; arg < n_args; arg ++ ) { + const operand_info &op_info = pI->operand_lookup(n_return+1+arg); + if( !op_info.is_reg() ) { + printf("GPGPU-Sim PTX: Execution error - no support for non-register opernands for call/return\n"); + abort(); + } + const symbol *dst_reg = target_func->get_arg(arg); + + ptx_reg_t src_value = thread->get_operand_value(op_info); + arg_values.push_back( std::make_pair(dst_reg,src_value) ); + } + + // note register for corresponding return instruction to place result into + const symbol *return_var_src = NULL; + const symbol *return_var_dst = NULL; + if( target_func->has_return() ) { + if( !pI->dst().is_reg() ) { + printf("GPGPU-Sim PTX: Execution error - no support for non-register dst reg for call\n"); + abort(); + } + return_var_dst = pI->dst().get_symbol(); + return_var_src = target_func->get_return_var(); + } + + unsigned sid = thread->get_hw_sid(); + unsigned tid = thread->get_hw_tid(); + unsigned callee_pc=0, callee_rpc=0; + if( gpgpu_simd_model == POST_DOMINATOR ) { + get_pdom_stack_top_info(sid,tid,&callee_pc,&callee_rpc); + assert( callee_pc == thread->get_pc() ); + } + + thread->callstack_push(callee_pc+1,callee_rpc,return_var_src,return_var_dst,call_uid_next++); + + std::list< std::pair<const symbol*, ptx_reg_t> >::iterator a; + for( a=arg_values.begin(); a != arg_values.end(); a++ ) { + const symbol *dst_reg = a->first; + ptx_reg_t value = a->second; + thread->set_operand_value(dst_reg,value); + } + + thread->set_npc(target_func); +} + +void cnot_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case PRED_TYPE: d.pred = (a.pred == 0)?1:0; break; + case B16_TYPE: d.u16 = (a.u16 == 0)?1:0; break; + case B32_TYPE: d.u32 = (a.u32 == 0)?1:0; break; + case B64_TYPE: d.u64 = (a.u64 == 0)?1:0; break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); // TODO: add more typechecking like this + break; + } + + thread->set_operand_value(dst,d); +} + +void cos_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + d.f32 = cos(a.f32); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +ptx_reg_t chop( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + switch ( to_width ) { + case 8: x.mask_and(0,0xFF); break; + case 16: x.mask_and(0,0xFFFF); break; + case 32: x.mask_and(0,0xFFFFFFFF); break; + case 64: break; + default: assert(0); + } + return x; +} + +ptx_reg_t sext( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + x=chop(x,0,from_width,0,rounding_mode,saturation_mode); + switch ( from_width ) { + case 8: if ( x.get_bit(7) ) x.mask_or(0xFFFFFFFF,0xFFFFFF00);break; + case 16:if ( x.get_bit(15) ) x.mask_or(0xFFFFFFFF,0xFFFF0000);break; + case 32: if ( x.get_bit(31) ) x.mask_or(0xFFFFFFFF,0x00000000);break; + case 64: break; + default: assert(0); + } + return x; +} + +ptx_reg_t zext( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + return chop(x,0,from_width,0,rounding_mode,saturation_mode); +} + +int saturatei(int a, int max, int min) +{ + if (a > max) a = max; + else if (a < min) a = min; + return a; +} + +unsigned int saturatei(unsigned int a, unsigned int max) +{ + if (a > max) a = max; + return a; +} + +ptx_reg_t f2x( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + assert( from_width == 32); + + enum cuda_math::cudaRoundMode mode = cuda_math::cudaRoundZero; + switch (rounding_mode) { + case RZI_OPTION: mode = cuda_math::cudaRoundZero; break; + case RNI_OPTION: mode = cuda_math::cudaRoundNearest; break; + case RMI_OPTION: mode = cuda_math::cudaRoundMinInf; break; + case RPI_OPTION: mode = cuda_math::cudaRoundPosInf; break; + default: break; + } + + ptx_reg_t y; + if ( to_sign == 1 ) { // convert to 64-bit number first? + int tmp = cuda_math::__internal_float2int(x.f32, mode); + if ((x.u32 & 0x7f800000) == 0) + tmp = 0; // round denorm. FP to 0 + if (saturation_mode && to_width < 32) { + tmp = saturatei(tmp, (1<<to_width) - 1, -(1<<to_width)); + } + switch ( to_width ) { + case 8: y.s8 = (char)tmp; break; + case 16: y.s16 = (short)tmp; break; + case 32: y.s32 = (int)tmp; break; + case 64: y.s64 = (long long)tmp; break; + default: assert(0); break; + } + } else if ( to_sign == 0 ) { + unsigned int tmp = cuda_math::__internal_float2uint(x.f32, mode); + if ((x.u32 & 0x7f800000) == 0) + tmp = 0; // round denorm. FP to 0 + if (saturation_mode && to_width < 32) { + tmp = saturatei(tmp, (1<<to_width) - 1); + } + switch ( to_width ) { + case 8: y.u8 = (unsigned char)tmp; break; + case 16: y.u16 = (unsigned short)tmp; break; + case 32: y.u32 = (unsigned int)tmp; break; + case 64: y.u64 = (unsigned long long)tmp; break; + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: assert(0); break; // handled by f2f + case 64: + y.f64 = x.f32; + break; + default: assert(0); break; + } + } + return y; +} + +double saturated2i (double a, double max, double min) { + if (a > max) a = max; + else if (a < min) a = min; + return a; +} + +ptx_reg_t d2x( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + assert( from_width == 64); + + double tmp; + switch (rounding_mode) { + case RZI_OPTION: tmp = trunc(x.f64); break; + case RNI_OPTION: tmp = nearbyint(x.f64); break; + case RMI_OPTION: tmp = floor(x.f64); break; + case RPI_OPTION: tmp = ceil(x.f64); break; + default: tmp = x.f64; break; + } + + ptx_reg_t y; + if ( to_sign == 1 ) { + tmp = saturated2i(tmp, ((1<<(to_width - 1)) - 1), (1<<(to_width - 1)) ); + switch ( to_width ) { + case 8: y.s8 = (char)tmp; break; + case 16: y.s16 = (short)tmp; break; + case 32: y.s32 = (int)tmp; break; + case 64: y.s64 = (long long)tmp; break; + default: assert(0); break; + } + } else if ( to_sign == 0 ) { + tmp = saturated2i(tmp, ((1<<(to_width - 1)) - 1), 0); + switch ( to_width ) { + case 8: y.u8 = (unsigned char)tmp; break; + case 16: y.u16 = (unsigned short)tmp; break; + case 32: y.u32 = (unsigned int)tmp; break; + case 64: y.u64 = (unsigned long long)tmp; break; + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: + y.f32 = x.f64; + break; + case 64: + y.f64 = x.f64; // should be handled by d2d + break; + default: assert(0); break; + } + } + return y; +} + +ptx_reg_t s2f( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + + if (from_width < 64) { // 32-bit conversion + y = sext(x,from_width,32,0,rounding_mode,saturation_mode); + + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__int2float_rz(y.s32); break; + case RN_OPTION: y.f32 = cuda_math::__int2float_rn(y.s32); break; + case RM_OPTION: y.f32 = cuda_math::__int2float_rd(y.s32); break; + case RP_OPTION: y.f32 = cuda_math::__int2float_ru(y.s32); break; + default: break; + } + break; + case 64: y.f64 = y.s32; break; // no rounding needed + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + case RN_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + case RM_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + case RP_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + default: break; + } + break; + case 64: y.f64 = y.s64; break; // no internal implementation found + default: assert(0); break; + } + } + + // saturating an integer to 1 or 0? + return y; +} + +ptx_reg_t u2f( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + + if (from_width < 64) { // 32-bit conversion + y = zext(x,from_width,32,0,rounding_mode,saturation_mode); + + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__uint2float_rz(y.u32); break; + case RN_OPTION: y.f32 = cuda_math::__uint2float_rn(y.u32); break; + case RM_OPTION: y.f32 = cuda_math::__uint2float_rd(y.u32); break; + case RP_OPTION: y.f32 = cuda_math::__uint2float_ru(y.u32); break; + default: break; + } + break; + case 64: y.f64 = y.u32; break; // no rounding needed + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + case RN_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + case RM_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + case RP_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + default: break; + } + break; + case 64: y.f64 = y.u64; break; // no internal implementation found + default: assert(0); break; + } + } + + // saturating an integer to 1 or 0? + return y; +} + +ptx_reg_t f2f( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + switch ( rounding_mode ) { + case RZI_OPTION: + y.f32 = truncf(x.f32); + break; + case RNI_OPTION: + y.f32 = cuda_math::__internal_nearbyintf(x.f32); + break; + case RMI_OPTION: + if ((x.u32 & 0x7f800000) == 0) { + y.u32 = x.u32 & 0x80000000; // round denorm. FP to 0, keeping sign + } else { + y.f32 = floorf(x.f32); + } + break; + case RPI_OPTION: + if ((x.u32 & 0x7f800000) == 0) { + y.u32 = x.u32 & 0x80000000; // round denorm. FP to 0, keeping sign + } else { + y.f32 = ceilf(x.f32); + } + break; + default: + if ((x.u32 & 0x7f800000) == 0) { + y.u32 = x.u32 & 0x80000000; // round denorm. FP to 0, keeping sign + } else { + y.f32 = x.f32; + } + break; + } + if (cuda_math::__cuda___isnanf(y.f32)) { + y.u32 = 0x7fffffff; + } else if (saturation_mode) { + y.f32 = cuda_math::__saturatef(y.f32); + } + + return y; +} + +ptx_reg_t d2d( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + switch ( rounding_mode ) { + case RZI_OPTION: + y.f64 = trunc(x.f64); + break; + case RNI_OPTION: + y.f64 = cuda_math::__internal_nearbyintf(x.f64); + break; + case RMI_OPTION: + y.f64 = floor(x.f64); + break; + case RPI_OPTION: + y.f64 = ceil(x.f64); + break; + default: + y.f64 = x.f64; + break; + } + if (isnan(y.f64)) { + y.u64 = 0xfff8000000000000ull; + } else if (saturation_mode) { + y.f64 = cuda_math::__saturatef(y.f64); + } + return y; +} + +ptx_reg_t (*g_cvt_fn[11][11])( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, + int rounding_mode, int saturation_mode ) = { + { NULL, sext, sext, sext, NULL, sext, sext, sext, s2f, s2f, s2f}, + { chop, NULL, sext, sext, chop, NULL, sext, sext, s2f, s2f, s2f}, + { chop, chop, NULL, sext, chop, chop, NULL, sext, s2f, s2f, s2f}, + { chop, chop, chop, NULL, chop, chop, chop, NULL, s2f, s2f, s2f}, + { NULL, zext, zext, zext, NULL, zext, zext, zext, u2f, u2f, u2f}, + { chop, NULL, zext, zext, chop, NULL, zext, zext, u2f, u2f, u2f}, + { chop, chop, NULL, zext, chop, chop, NULL, zext, u2f, u2f, u2f}, + { chop, chop, chop, NULL, chop, chop, chop, NULL, u2f, u2f, u2f}, + { f2x , f2x , f2x , f2x , f2x , f2x , f2x , f2x , NULL,f2x, f2x}, + { f2x , f2x , f2x , f2x , f2x , f2x , f2x , f2x , f2x, f2f, f2x}, + { d2x , d2x , d2x , d2x , d2x , d2x , d2x , d2x , d2x, d2x, d2d} +}; + +unsigned type_decode( unsigned type, size_t &size, int &basic_type ) +{ + switch ( type ) { + case S8_TYPE: size=8; basic_type=1; return 0; + case S16_TYPE: size=16; basic_type=1; return 1; + case S32_TYPE: size=32; basic_type=1; return 2; + case S64_TYPE: size=64; basic_type=1; return 3; + case U8_TYPE: size=8; basic_type=0; return 4; + case U16_TYPE: size=16; basic_type=0; return 5; + case U32_TYPE: size=32; basic_type=0; return 6; + case U64_TYPE: size=64; basic_type=0; return 7; + case F16_TYPE: size=16; basic_type=-1; return 8; + case F32_TYPE: size=32; basic_type=-1; return 9; + case F64_TYPE: size=64; basic_type=-1; return 10; + case PRED_TYPE: size=1; basic_type=2; return 11; + case B8_TYPE: size=8; basic_type=0; return 12; + case B16_TYPE: size=16; basic_type=0; return 13; + case B32_TYPE: size=32; basic_type=0; return 14; + case B64_TYPE: size=64; basic_type=0; return 15; + default: + printf("ERROR ** type_decode() does not know about \"%s\"\n", g_ptx_token_decode[type].c_str() ); + assert(0); + return 0xDEADBEEF; + } +} + +void ptx_round(ptx_reg_t& data, int rounding_mode, int type) +{ + if (rounding_mode == RN_OPTION) { + return; + } + switch ( rounding_mode ) { + case RZI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + data.f32 = truncf(data.f32); + break; + case F64_TYPE: + if (data.f64 < 0) data.f64 = ceil(data.f64); //negative + else data.f64 = floor(data.f64); //positive + break; + default: assert(0); break; + } + break; + case RNI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = cuda_math::__cuda_nearbyintf(data.f32); break; + case F64_TYPE: data.f64 = round(data.f64); break; + default: assert(0); break; + } + break; + case RMI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + data.f32 = floorf(data.f32); + break; + case F64_TYPE: data.f64 = floor(data.f64); break; + default: assert(0); break; + } + break; + case RPI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = ceilf(data.f32); break; + case F64_TYPE: data.f64 = ceil(data.f64); break; + default: assert(0); break; + } + break; + default: break; + } + + if (type == F32_TYPE) { + if (cuda_math::__cuda___isnanf(data.f32)) { + data.u32 = 0x7fffffff; + } + } + if (type == F64_TYPE) { + if (isnan(data.f64)) { + data.u64 = 0xfff8000000000000ull; + } + } +} + +void ptx_saturate(ptx_reg_t& data, int saturation_mode, int type) +{ + if (!saturation_mode) { + return; + } + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to clamp an integer to 1??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + if (data.f32 > 1.0f) data.f32 = 1.0f; //negative + if (data.f32 < 0.0f) data.f32 = 0.0f; //positive + break; + case F64_TYPE: + if (data.f64 > 1.0f) data.f64 = 1.0f; //negative + if (data.f64 < 0.0f) data.f64 = 0.0f; //positive + break; + default: assert(0); break; + } + +} + +void cvt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + unsigned to_type = pI->get_type(); + unsigned from_type = pI->get_type2(); + unsigned rounding_mode = pI->rounding_mode(); + unsigned saturation_mode = pI->saturation_mode(); + + if ( to_type == F16_TYPE || from_type == F16_TYPE ) + abort(); + + int to_sign, from_sign; + size_t from_width, to_width; + unsigned src_fmt = type_decode(from_type, from_width, from_sign); + unsigned dst_fmt = type_decode(to_type, to_width, to_sign); + + ptx_reg_t data = thread->get_operand_value(src1); + if ( g_cvt_fn[src_fmt][dst_fmt] != NULL ) { + ptx_reg_t result = g_cvt_fn[src_fmt][dst_fmt](data,from_width,to_width,to_sign, rounding_mode, saturation_mode); + data = result; + } + + thread->set_operand_value(dst,data); +} + +void div_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + ptx_reg_t src1_data = thread->get_operand_value(src1); + ptx_reg_t src2_data = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + data.s64 = src1_data.s64 / src2_data.s64; break; + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + case B8_TYPE: + case B16_TYPE: + case B32_TYPE: + case B64_TYPE: + data.u64 = src1_data.u64 / src2_data.u64; break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = src1_data.f32 / src2_data.f32; break; + case F64_TYPE: data.f64 = src1_data.f64 / src2_data.f64; break; + default: assert(0); break; + } + thread->set_operand_value(dst,data); +} + +void ex2_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + src1_data = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + data.f32 = cuda_math::__powf(2.0, src1_data.f32); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,data); +} + +void exit_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + core_t *sc = thread->get_core(); + unsigned warp_id = thread->get_hw_wid(); + sc->warp_exit(warp_id); + + thread->m_cta_info->register_thread_exit(thread); + thread->set_done(); +} + +void ld_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + ptx_reg_t src1_data = thread->get_operand_value(src1); + ptx_reg_t data; + unsigned space = pI->get_space(); + unsigned vector_spec = pI->get_vector(); + unsigned type = pI->get_type(); + memory_space *mem = NULL; + + switch ( space ) { + case GLOBAL_DIRECTIVE: mem = g_global_mem; break; + case LOCAL_DIRECTIVE: mem = thread->m_local_mem; break; + case TEX_DIRECTIVE: mem = g_tex_mem; break; + case SURF_DIRECTIVE: mem = g_surf_mem; break; + case PARAM_DIRECTIVE: mem = g_param_mem; break; + case SHARED_DIRECTIVE: mem = thread->m_shared_mem; break; + case CONST_DIRECTIVE: mem = g_global_mem; break; + default: + assert(0); + break; + } + + size_t size; + int t; + data.u64=0; + type_decode(type,size,t); + if (!vector_spec) { + mem->read(src1_data.u32,size/8,&data.s64); + if( type == S16_TYPE || type == S32_TYPE ) + sign_extend(data,size,dst); + thread->set_operand_value(dst,data); + } else { + ptx_reg_t data1, data2, data3, data4; + mem->read(src1_data.u32,size/8,&data1.s64); + mem->read(src1_data.u32+size/8,size/8,&data2.s64); + if (vector_spec != V2_TYPE) { //either V3 or V4 + mem->read(src1_data.u32+2*size/8,size/8,&data3.s64); + if (vector_spec != V3_TYPE) { //v4 + mem->read(src1_data.u32+3*size/8,size/8,&data4.s64); + thread->set_vector_operand_values(dst,data1,data2,data3,data4, 4); + } else //v3 + thread->set_vector_operand_values(dst,data1,data2,data3,data3,3); + } else //v2 + thread->set_vector_operand_values(dst,data1,data2,data2,data2,2); + } + thread->m_last_effective_address = src1_data.u32; + thread->m_last_memory_space = space; +} + +void lg2_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + d.f32 = log(a.f32)/log(2); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void mad24_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + ptx_reg_t d, t; + ptx_reg_t a = thread->get_operand_value(src1); + ptx_reg_t b = thread->get_operand_value(src2); + ptx_reg_t c = thread->get_operand_value(src3); + + unsigned i_type = pI->get_type(); + unsigned sat_mode = pI->saturation_mode(); + + assert( !pI->is_wide() ); + + switch ( i_type ) { + case S32_TYPE: + t.s64 = a.s32 * b.s32; + if ( pI->is_hi() ) { + d.s64 = (t.s64>>16) + c.s32; + if ( sat_mode ) { + if ( d.s64 > (int)0x7FFFFFFF ) + d.s64 = (int)0x7FFFFFFF; + else if ( d.s64 < (int)0x80000000 ) + d.s64 = (int)0x80000000; + } + } else if ( pI->is_lo() ) d.s64 = t.s32 + c.s32; + else assert(0); + break; + case U32_TYPE: + t.u64 = a.u32 * b.u32; + if ( pI->is_hi() ) d.u64 = (t.u64>>16) + c.u32; + else if ( pI->is_lo() ) d.u64 = t.u32 + c.u32; + else assert(0); + break; + default: + assert(0); + break; + } + thread->set_operand_value(dst,d); +} + +void mad_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + ptx_reg_t d, t; + ptx_reg_t a = thread->get_operand_value(src1); + ptx_reg_t b = thread->get_operand_value(src2); + ptx_reg_t c = thread->get_operand_value(src3); + + unsigned i_type = pI->get_type(); + unsigned rounding_mode = pI->rounding_mode(); + + switch ( i_type ) { + case S16_TYPE: + t.s32 = a.s16 * b.s16; + if ( pI->is_wide() ) d.s32 = t.s32 + c.s32; + else if ( pI->is_hi() ) d.s16 = (t.s32>>16) + c.s16; + else if ( pI->is_lo() ) d.s16 = t.s16 + c.s16; + else assert(0); + break; + case S32_TYPE: + t.s64 = a.s32 * b.s32; + if ( pI->is_wide() ) d.s64 = t.s64 + c.s64; + else if ( pI->is_hi() ) d.s32 = (t.s64>>32) + c.s32; + else if ( pI->is_lo() ) d.s32 = t.s32 + c.s32; + else assert(0); + break; + case S64_TYPE: + t.s64 = a.s64 * b.s64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.s64 = t.s64 + c.s64; + else assert(0); + break; + case U16_TYPE: + t.u32 = a.u16 * b.u16; + if ( pI->is_wide() ) d.u32 = t.u32 + c.u32; + else if ( pI->is_hi() ) d.u16 = (t.u32>>16) + c.u16; + else if ( pI->is_lo() ) d.u16 = t.u16 + c.u16; + else assert(0); + break; + case U32_TYPE: + t.u64 = a.u32 * b.u32; + if ( pI->is_wide() ) d.u64 = t.u64 + c.u64; + else if ( pI->is_hi() ) d.u32 = (t.u64>>32) + c.u32; + else if ( pI->is_lo() ) d.u32 = t.u32 + c.u32; + else assert(0); + break; + case U64_TYPE: + t.u64 = a.u64 * b.u64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.u64 = t.u64 + c.u64; + else assert(0); + break; + case F16_TYPE: + assert(0); + break; + case F32_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f32 = a.f32 * b.f32 + c.f32; + if ( pI->saturation_mode() ) { + if ( d.f32 < 0 ) d.f32 = 0; + else if ( d.f32 > 1.0f ) d.f32 = 1.0f; + } + fesetround( orig_rm ); + break; + } + case F64_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f64 = a.f64 * b.f64 + c.f64; + if ( pI->saturation_mode() ) { + if ( d.f64 < 0 ) d.f64 = 0; + else if ( d.f64 > 1.0f ) d.f64 = 1.0; + } + fesetround( orig_rm ); + break; + } + default: + assert(0); + break; + } + thread->set_operand_value(dst,d); +} + +bool isNaN(float x) +{ + return isnan(x); +} + +bool isNaN(double x) +{ + return isnan(x); +} + +void max_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: d.u16 = MY_MAX_I(a.u16,b.u16); break; + case U32_TYPE: d.u32 = MY_MAX_I(a.u32,b.u32); break; + case U64_TYPE: d.u64 = MY_MAX_I(a.u64,b.u64); break; + case S16_TYPE: d.s16 = MY_MAX_I(a.s16,b.s16); break; + case S32_TYPE: d.s32 = MY_MAX_I(a.s32,b.s32); break; + case S64_TYPE: d.s64 = MY_MAX_I(a.s64,b.s64); break; + case F32_TYPE: d.f32 = MY_MAX_F(a.f32,b.f32); break; + case F64_TYPE: d.f64 = MY_MAX_F(a.f64,b.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void membar_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + /* TODO: add timing model support */ +} + +void min_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: d.u16 = MY_MIN_I(a.u16,b.u16); break; + case U32_TYPE: d.u32 = MY_MIN_I(a.u32,b.u32); break; + case U64_TYPE: d.u64 = MY_MIN_I(a.u64,b.u64); break; + case S16_TYPE: d.s16 = MY_MIN_I(a.s16,b.s16); break; + case S32_TYPE: d.s32 = MY_MIN_I(a.s32,b.s32); break; + case S64_TYPE: d.s64 = MY_MIN_I(a.s64,b.s64); break; + case F32_TYPE: d.f32 = MY_MIN_F(a.f32,b.f32); break; + case F64_TYPE: d.f64 = MY_MIN_F(a.f64,b.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void mov_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + + if( src1.is_vector() || dst.is_vector() ) { + // pack or unpack operation + unsigned nbits_to_move; + ptx_reg_t tmp_bits; + + switch( pI->get_type() ) { + case B16_TYPE: nbits_to_move = 16; break; + case B32_TYPE: nbits_to_move = 32; break; + case B64_TYPE: nbits_to_move = 64; break; + default: printf("Execution error: mov pack/unpack with unsupported type qualifier\n"); assert(0); break; + } + + if( src1.is_vector() ) { + unsigned nelem = src1.get_vect_nelem(); + ptx_reg_t v[4]; + thread->get_vector_operand_values(src1, v, nelem ); + + unsigned bits_per_src_elem = nbits_to_move / nelem; + for( unsigned i=0; i < nelem; i++ ) { + switch(bits_per_src_elem) { + case 8: tmp_bits.u64 |= ((unsigned long long)(v[i].u8) << (8*i)); break; + case 16: tmp_bits.u64 |= ((unsigned long long)(v[i].u16) << (16*i)); break; + case 32: tmp_bits.u64 |= ((unsigned long long)(v[i].u32) << (32*i)); break; + default: printf("Execution error: mov pack/unpack with unsupported source/dst size ratio (src)\n"); assert(0); break; + } + } + } else { + data = thread->get_operand_value(src1); + + switch( pI->get_type() ) { + case B16_TYPE: tmp_bits.u16 = data.u16; break; + case B32_TYPE: tmp_bits.u32 = data.u32; break; + case B64_TYPE: tmp_bits.u64 = data.u64; break; + default: assert(0); break; + } + } + + if( dst.is_vector() ) { + unsigned nelem = dst.get_vect_nelem(); + ptx_reg_t v[4]; + unsigned bits_per_dst_elem = nbits_to_move / nelem; + for( unsigned i=0; i < nelem; i++ ) { + switch(bits_per_dst_elem) { + case 8: v[i].u8 = tmp_bits.u64 & (((unsigned long long) 0xFF) << (8*i)); break; + case 16: v[i].u16 = tmp_bits.u64 & (((unsigned long long) 0xFFFF) << (16*i)); break; + case 32: v[i].u32 = tmp_bits.u64 & (((unsigned long long) 0xFFFFFFFF) << (32*i)); break; + default: + printf("Execution error: mov pack/unpack with unsupported source/dst size ratio (dst)\n"); + assert(0); + break; + } + } + thread->set_vector_operand_values(dst,v[0],v[1],v[2],v[3],nelem); + } else { + thread->set_operand_value(dst,tmp_bits); + } + } else { + data = thread->get_operand_value(src1); + thread->set_operand_value(dst,data); + } +} + +void mul24_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + src1_data.mask_and(0,0x00FFFFFF); + src2_data.mask_and(0,0x00FFFFFF); + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case S32_TYPE: + if( src1_data.get_bit(23) ) + src1_data.mask_or(0xFFFFFFFF,0xFF000000); + if( src2_data.get_bit(23) ) + src2_data.mask_or(0xFFFFFFFF,0xFF000000); + data.s64 = src1_data.s64 * src2_data.s64; + break; + case U32_TYPE: + data.u64 = src1_data.u64 * src2_data.u64; + break; + default: + printf("GPGPU-Sim PTX: Execution error - type mismatch with instruction\n"); + assert(0); + break; + } + + if ( pI->is_hi() ) { + data.u64 = data.u64 >> 16; + data.mask_and(0,0xFFFFFFFF); + } else if (pI->is_lo()) { + data.mask_and(0,0xFFFFFFFF); + } + + thread->set_operand_value(dst,data); +} + +void mul_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + ptx_reg_t d, t; + ptx_reg_t a = thread->get_operand_value(src1); + ptx_reg_t b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + unsigned rounding_mode = pI->rounding_mode(); + + switch ( i_type ) { + case S16_TYPE: + t.s32 = ((int)a.s16) * ((int)b.s16); + if ( pI->is_wide() ) d.s32 = t.s32; + else if ( pI->is_hi() ) d.s16 = (t.s32>>16); + else if ( pI->is_lo() ) d.s16 = t.s16; + else assert(0); + break; + case S32_TYPE: + t.s64 = ((long long)a.s32) * ((long long)b.s32); + if ( pI->is_wide() ) d.s64 = t.s64; + else if ( pI->is_hi() ) d.s32 = (t.s64>>32); + else if ( pI->is_lo() ) d.s32 = t.s32; + else assert(0); + break; + case S64_TYPE: + t.s64 = a.s64 * b.s64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.s64 = t.s64; + else assert(0); + break; + case U16_TYPE: + t.u32 = ((unsigned)a.u16) * ((unsigned)b.u16); + if ( pI->is_wide() ) d.u32 = t.u32; + else if ( pI->is_lo() ) d.u16 = t.u16; + else if ( pI->is_hi() ) d.u16 = (t.u32>>16); + else assert(0); + break; + case U32_TYPE: + t.u64 = ((unsigned long long)a.u32) * ((unsigned long long)b.u32); + if ( pI->is_wide() ) d.u64 = t.u64; + else if ( pI->is_lo() ) d.u32 = t.u32; + else if ( pI->is_hi() ) d.u32 = (t.u64>>32); + else assert(0); + break; + case U64_TYPE: + t.u64 = a.u64 * b.u64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.u64 = t.u64; + else assert(0); + break; + case F16_TYPE: + assert(0); + break; + case F32_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f32 = a.f32 * b.f32; + if ( pI->saturation_mode() ) { + if ( d.f32 < 0 ) d.f32 = 0; + else if ( d.f32 > 1.0f ) d.f32 = 1.0f; + } + fesetround( orig_rm ); + break; + } + case F64_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f64 = a.f64 * b.f64; + if ( pI->saturation_mode() ) { + if ( d.f64 < 0 ) d.f64 = 0; + else if ( d.f64 > 1.0f ) d.f64 = 1.0; + } + fesetround( orig_rm ); + break; + } + default: + assert(0); + break; + } + thread->set_operand_value(dst,d); +} + +void neg_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + + src1_data = thread->get_operand_value(src1); + + unsigned to_type = pI->get_type(); + switch ( to_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + data.s64 = 0 - src1_data.s64; break; // seems buggy, but not (just ignore higher bits) + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = 0.0f - src1_data.f32; break; + case F64_TYPE: data.f64 = 0.0f - src1_data.f64; break; + default: assert(0); break; + } + + thread->set_operand_value(dst,data); +} + +void not_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case PRED_TYPE: d.pred = ~a.pred; break; + case B16_TYPE: d.u16 = ~a.u16; break; + case B32_TYPE: d.u32 = ~a.u32; break; + case B64_TYPE: d.u64 = ~a.u64; break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void or_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 | src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void rcp_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + src1_data = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + data.f32 = 1.0f / src1_data.f32; + break; + case F64_TYPE: + data.f64 = 1.0f / src1_data.f64; + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,data); +} + +void red_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +void rem_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 % src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void ret_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + bool empty = thread->callstack_pop(); + if( empty ) { + core_t *sc = thread->get_core(); + unsigned warp_id = thread->get_hw_wid(); + sc->warp_exit(warp_id); + thread->m_cta_info->register_thread_exit(thread); + thread->set_done(); + } +} + +void rsqrt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + if ( a.f32 < 0 ) { + d.u64 = 0; + d.u64 = 0x7fc00000; // NaN + } else if ( a.f32 == 0 ) { + d.u64 = 0; + d.u32 = 0x7f800000; // Inf + } else + d.f32 = cuda_math::__internal_accurate_fdividef(1.0f, sqrtf(a.f32)); + break; + case F64_TYPE: + if ( a.f32 < 0 ) { + d.u64 = 0; + d.u32 = 0x7fc00000; // NaN + float x = d.f32; + d.f64 = (double)x; + } else if ( a.f32 == 0 ) { + d.u64 = 0; + d.u32 = 0x7f800000; // Inf + float x = d.f32; + d.f64 = (double)x; + } else + d.f64 = 1.0 / sqrt(a.f64); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +#define SAD(d,a,b,c) d = c + ((a<b) ? (b-a) : (a-b)) + +void sad_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, c, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src1(); + const operand_info &src3 = pI->src1(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + c = thread->get_operand_value(src3); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: SAD(d.u16,a.u16,b.u16,c.u16); break; + case U32_TYPE: SAD(d.u32,a.u32,b.u32,c.u32); break; + case U64_TYPE: SAD(d.u64,a.u64,b.u64,c.u64); break; + case S16_TYPE: SAD(d.s16,a.s16,b.s16,c.s16); break; + case S32_TYPE: SAD(d.s32,a.s32,b.s32,c.s32); break; + case S64_TYPE: SAD(d.s64,a.s64,b.s64,c.s64); break; + case F32_TYPE: SAD(d.f32,a.f32,b.f32,c.f32); break; + case F64_TYPE: SAD(d.f64,a.f64,b.f64,c.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void selp_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + + ptx_reg_t a, b, c, d; + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + c = thread->get_operand_value(src3); + + d = (c.pred)?a:b; + + thread->set_operand_value(dst,d); +} + +bool isFloat(int type) +{ + switch ( type ) { + case F16_TYPE: + case F32_TYPE: + case F64_TYPE: + return true; + default: + return false; + } +} + +bool CmpOp( int type, ptx_reg_t a, ptx_reg_t b, unsigned cmpop ) +{ + bool t = false; + + switch ( type ) { + case B16_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u16 == b.u16); break; + case NE_OPTION: t = (a.u16 != b.u16); break; + default: + assert(0); + } + + case B32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u32 == b.u32); break; + case NE_OPTION: t = (a.u32 != b.u32); break; + default: + assert(0); + } + case B64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u64 == b.u64); break; + case NE_OPTION: t = (a.u64 != b.u64); break; + default: + assert(0); + } + break; + case S8_TYPE: + case S16_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.s16 == b.s16); break; + case NE_OPTION: t = (a.s16 != b.s16); break; + case LT_OPTION: t = (a.s16 < b.s16); break; + case LE_OPTION: t = (a.s16 <= b.s16); break; + case GT_OPTION: t = (a.s16 > b.s16); break; + case GE_OPTION: t = (a.s16 >= b.s16); break; + default: + assert(0); + } + break; + case S32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.s32 == b.s32); break; + case NE_OPTION: t = (a.s32 != b.s32); break; + case LT_OPTION: t = (a.s32 < b.s32); break; + case LE_OPTION: t = (a.s32 <= b.s32); break; + case GT_OPTION: t = (a.s32 > b.s32); break; + case GE_OPTION: t = (a.s32 >= b.s32); break; + default: + assert(0); + } + break; + case S64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.s64 == b.s64); break; + case NE_OPTION: t = (a.s64 != b.s64); break; + case LT_OPTION: t = (a.s64 < b.s64); break; + case LE_OPTION: t = (a.s64 <= b.s64); break; + case GT_OPTION: t = (a.s64 > b.s64); break; + case GE_OPTION: t = (a.s64 >= b.s64); break; + default: + assert(0); + } + break; + case U8_TYPE: + case U16_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u16 == b.u16); break; + case NE_OPTION: t = (a.u16 != b.u16); break; + case LT_OPTION: t = (a.u16 < b.u16); break; + case LE_OPTION: t = (a.u16 <= b.u16); break; + case GT_OPTION: t = (a.u16 > b.u16); break; + case GE_OPTION: t = (a.u16 >= b.u16); break; + case LO_OPTION: t = (a.u16 < b.u16); break; + case LS_OPTION: t = (a.u16 <= b.u16); break; + case HI_OPTION: t = (a.u16 > b.u16); break; + case HS_OPTION: t = (a.u16 >= b.u16); break; + default: + assert(0); + } + break; + case U32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u32 == b.u32); break; + case NE_OPTION: t = (a.u32 != b.u32); break; + case LT_OPTION: t = (a.u32 < b.u32); break; + case LE_OPTION: t = (a.u32 <= b.u32); break; + case GT_OPTION: t = (a.u32 > b.u32); break; + case GE_OPTION: t = (a.u32 >= b.u32); break; + case LO_OPTION: t = (a.u32 < b.u32); break; + case LS_OPTION: t = (a.u32 <= b.u32); break; + case HI_OPTION: t = (a.u32 > b.u32); break; + case HS_OPTION: t = (a.u32 >= b.u32); break; + default: + assert(0); + } + break; + case U64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u64 == b.u64); break; + case NE_OPTION: t = (a.u64 != b.u64); break; + case LT_OPTION: t = (a.u64 < b.u64); break; + case LE_OPTION: t = (a.u64 <= b.u64); break; + case GT_OPTION: t = (a.u64 > b.u64); break; + case GE_OPTION: t = (a.u64 >= b.u64); break; + case LO_OPTION: t = (a.u64 < b.u64); break; + case LS_OPTION: t = (a.u64 <= b.u64); break; + case HI_OPTION: t = (a.u64 > b.u64); break; + case HS_OPTION: t = (a.u64 >= b.u64); break; + default: + assert(0); + } + break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.f32 == b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case NE_OPTION: t = (a.f32 != b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case LT_OPTION: t = (a.f32 < b.f32 ) && !isNaN(a.f32) && !isNaN(b.f32); break; + case LE_OPTION: t = (a.f32 <= b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case GT_OPTION: t = (a.f32 > b.f32 ) && !isNaN(a.f32) && !isNaN(b.f32); break; + case GE_OPTION: t = (a.f32 >= b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case EQU_OPTION: t = (a.f32 == b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case NEU_OPTION: t = (a.f32 != b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case LTU_OPTION: t = (a.f32 < b.f32 ) || isNaN(a.f32) || isNaN(b.f32); break; + case LEU_OPTION: t = (a.f32 <= b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case GTU_OPTION: t = (a.f32 > b.f32 ) || isNaN(a.f32) || isNaN(b.f32); break; + case GEU_OPTION: t = (a.f32 >= b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case NUM_OPTION: t = !isNaN(a.f32) && !isNaN(b.f32); break; + case NAN_OPTION: t = isNaN(a.f32) || isNaN(b.f32); break; + default: + assert(0); + } + break; + case F64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.f64 == b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case NE_OPTION: t = (a.f64 != b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case LT_OPTION: t = (a.f64 < b.f64 ) && !isNaN(a.f64) && !isNaN(b.f64); break; + case LE_OPTION: t = (a.f64 <= b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case GT_OPTION: t = (a.f64 > b.f64 ) && !isNaN(a.f64) && !isNaN(b.f64); break; + case GE_OPTION: t = (a.f64 >= b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case EQU_OPTION: t = (a.f64 == b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case NEU_OPTION: t = (a.f64 != b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case LTU_OPTION: t = (a.f64 < b.f64 ) || isNaN(a.f64) || isNaN(b.f64); break; + case LEU_OPTION: t = (a.f64 <= b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case GTU_OPTION: t = (a.f64 > b.f64 ) || isNaN(a.f64) || isNaN(b.f64); break; + case GEU_OPTION: t = (a.f64 >= b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case NUM_OPTION: t = !isNaN(a.f64) && !isNaN(b.f64); break; + case NAN_OPTION: t = isNaN(a.f64) || isNaN(b.f64); break; + default: + assert(0); + } + break; + default: assert(0); break; + } + + return t; +} + +void setp_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b; + + int t=0; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + assert( pI->get_num_operands() < 4 ); // or need to deal with "c" operand / boolOp + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned type = pI->get_type(); + unsigned cmpop = pI->get_cmpop(); + + t = CmpOp(type,a,b,cmpop); + + ptx_reg_t data; + data.pred = (t!=0); + + thread->set_operand_value(dst,data); +} + +void set_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b; + + int t=0; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + assert( pI->get_num_operands() < 4 ); // or need to deal with "c" operand / boolOp + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned src_type = pI->get_type2(); + unsigned cmpop = pI->get_cmpop(); + + t = CmpOp(src_type,a,b,cmpop); + + ptx_reg_t data; + if ( isFloat(pI->get_type()) ) { + data.f32 = (t!=0)?1.0f:0.0f; + } else { + data.u32 = (t!=0)?0xFFFFFFFF:0; + } + + thread->set_operand_value(dst,data); + +} + +void shl_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case B16_TYPE: + if ( b.u16 >= 16 ) + d.u16 = 0; + else + d.u16 = (unsigned short) ((a.u16 << b.u16) & 0xFFFF); + break; + case B32_TYPE: + if ( b.u32 >= 32 ) + d.u32 = 0; + else + d.u32 = (unsigned) ((a.u32 << b.u32) & 0xFFFFFFFF); + break; + case B64_TYPE: + if ( b.u32 >= 64 ) + d.u64 = 0; + else + d.u64 = (a.u64 << b.u64); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void shr_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: + case B16_TYPE: + if ( b.u16 < 16 ) + d.u16 = (unsigned short) ((a.u16 >> b.u16) & 0xFFFF); + else + d.u16 = 0; + break; + case U32_TYPE: + case B32_TYPE: + if ( b.u32 < 32 ) + d.u32 = (unsigned) ((a.u32 >> b.u32) & 0xFFFFFFFF); + else + d.u32 = 0; + break; + case U64_TYPE: + case B64_TYPE: + if ( b.u32 < 64 ) + d.u64 = (a.u64 >> b.u64); + else + d.u64 = 0; + break; + case S16_TYPE: + if ( b.u16 < 16 ) + d.s64 = (a.s16 >> b.s16); + else { + if ( a.s16 < 0 ) { + d.s64 = -1; + } else { + d.s64 = 0; + } + } + break; + case S32_TYPE: + if ( b.u32 < 32 ) + d.s64 = (a.s32 >> b.s32); + else { + if ( a.s32 < 0 ) { + d.s64 = -1; + } else { + d.s64 = 0; + } + } + break; + case S64_TYPE: + if ( b.u64 < 64 ) + d.s64 = (a.s64 >> b.u64); + else { + if ( a.s64 < 0 ) { + if ( b.s32 < 0 ) { + d.u64 = -1; + d.s32 = 0; + } else { + d.s64 = -1; + } + } else { + d.s64 = 0; + } + } + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void sin_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + d.f32 = sin(a.f32); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void slct_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + + ptx_reg_t a, b, c, d; + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + c = thread->get_operand_value(src3); + + bool t = false; + unsigned c_type = pI->get_type2(); + switch ( c_type ) { + case S32_TYPE: t = c.s32 >= 0; break; + case F32_TYPE: t = c.f32 >= 0; break; + default: assert(0); + } + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case B16_TYPE: + case U16_TYPE: d.u16 = t?a.u16:b.u16; break; + case F32_TYPE: + case B32_TYPE: + case U32_TYPE: d.u32 = t?a.u32:b.u32; break; + case F64_TYPE: + case B64_TYPE: + case U64_TYPE: d.u64 = t?a.u64:b.u64; break; + default: assert(0); + } + + thread->set_operand_value(dst,d); +} + +void sqrt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + if ( a.f32 < 0 ) + d.f32 = nanf(""); + else + d.f32 = sqrt(a.f32); break; + case F64_TYPE: + if ( a.f64 < 0 ) + d.f64 = nan(""); + else + d.f64 = sqrt(a.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void st_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); //may be scalar or vector of regs + ptx_reg_t addr = thread->get_operand_value(dst); + ptx_reg_t data; + unsigned space = pI->get_space(); + unsigned vector_spec = pI->get_vector(); + unsigned type = pI->get_type(); + memory_space *mem = NULL; + + switch ( space ) { + case GLOBAL_DIRECTIVE: mem = g_global_mem; break; + case LOCAL_DIRECTIVE: mem = thread->m_local_mem; break; + case TEX_DIRECTIVE: mem = g_tex_mem; break; + case SURF_DIRECTIVE: mem = g_surf_mem; break; + case PARAM_DIRECTIVE: mem = g_param_mem; break; + case SHARED_DIRECTIVE: mem = thread->m_shared_mem; break; + default: + assert(0); + break; + } + + size_t size; + int t; + type_decode(type,size,t); + + if (!vector_spec) { + data = thread->get_operand_value(src1); + mem->write(addr.u32,size/8,&data.s64); + } else { + if (vector_spec == V2_TYPE) { + ptx_reg_t* ptx_regs = new ptx_reg_t[2]; + thread->get_vector_operand_values(src1, ptx_regs, 2); + mem->write(addr.u32,size/8,&ptx_regs[0].s64); + mem->write(addr.u32+size/8,size/8,&ptx_regs[1].s64); + free(ptx_regs); + } + if (vector_spec == V3_TYPE) { + ptx_reg_t* ptx_regs = new ptx_reg_t[3]; + thread->get_vector_operand_values(src1, ptx_regs, 3); + mem->write(addr.u32,size/8,&ptx_regs[0].s64); + mem->write(addr.u32+size/8,size/8,&ptx_regs[1].s64); + mem->write(addr.u32+2*size/8,size/8,&ptx_regs[2].s64); + free(ptx_regs); + } + if (vector_spec == V4_TYPE) { + ptx_reg_t* ptx_regs = new ptx_reg_t[4]; + thread->get_vector_operand_values(src1, ptx_regs, 4); + mem->write(addr.u32,size/8,&ptx_regs[0].s64); + mem->write(addr.u32+size/8,size/8,&ptx_regs[1].s64); + mem->write(addr.u32+2*size/8,size/8,&ptx_regs[2].s64); + mem->write(addr.u32+3*size/8,size/8,&ptx_regs[3].s64); + free(ptx_regs); + } + } + thread->m_last_effective_address = addr.u32; + thread->m_last_memory_space = space; +} + +void sub_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + ptx_reg_t src1_data = thread->get_operand_value(src1); + ptx_reg_t src2_data = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + data.s64 = src1_data.s64 - src2_data.s64; break; + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + case B8_TYPE: + case B16_TYPE: + case B32_TYPE: + case B64_TYPE: + data.u64 = src1_data.u64 - src2_data.u64; break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = src1_data.f32 - src2_data.f32; break; + case F64_TYPE: data.f64 = src1_data.f64 - src2_data.f64; break; + default: assert(0); break; + } + + thread->set_operand_value(dst,data); +} + +ptx_reg_t* ptx_tex_regs = NULL; + +union intfloat { + int a; + float b; +}; + +float reduce_precision( float x, unsigned bits ) +{ + intfloat tmp; + tmp.b = x; + int v = tmp.a; + int man = v & ((1<<23)-1); + int mask = ((1<<bits)-1) << (23-bits); + int nv = (v & ((-1)-((1<<23)-1))) | (mask&man); + tmp.a = nv; + float result = tmp.b; + return result; +} + +unsigned wrap( unsigned x, unsigned y, unsigned mx, unsigned my ) +{ + unsigned nx = (mx+x)%mx; + unsigned ny = (my+y)%my; + return nx + mx*ny; +} + +void tex_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + cudasim_n_tex_insn++; + unsigned dimension = pI->dimension(); + const operand_info &dst = pI->dst(); //the registers to which fetched texel will be placed + const operand_info &src1 = pI->src1(); //the name of the texture + const operand_info &src2 = pI->src2(); //the vector registers containing coordinates of the texel to be fetched + + std::string texname = src1.name(); + unsigned to_type = pI->get_type(); + fflush(stdout); + ptx_reg_t data1, data2, data3, data4; + if (!ptx_tex_regs) ptx_tex_regs = new ptx_reg_t[4]; + thread->get_vector_operand_values(src2, ptx_tex_regs, 4); //ptx_reg should be 4 entry vector type...coordinates into texture + + assert(NameToTextureMap.find(texname) != NameToTextureMap.end());//use map to find texturerefence, then use map to find pointer to array + struct textureReference* texref = NameToTextureMap[texname]; + assert(TextureToArrayMap.find(texref) != TextureToArrayMap.end()); + struct cudaArray* cuArray = TextureToArrayMap[texref]; + assert(TextureToInfoMap.find(texref) != TextureToInfoMap.end()); + struct textureInfo* texInfo = TextureToInfoMap[texref]; + + //assume always 2D f32 input + //access array with src2 coordinates + memory_space *mem = g_global_mem; + float x_f32, y_f32; + size_t size; + int t; + unsigned tex_array_base; + unsigned int width = 0, height = 0; + int x = 0; + int y = 0; + unsigned tex_array_index; + float alpha=0, beta=0; + + type_decode(to_type,size,t); + tex_array_base = cuArray->devPtr32; + + switch (dimension) { + case GEOM_MODIFIER_1D: + x = ptx_tex_regs[0].u64; + x *= (cuArray->desc.w+cuArray->desc.x+cuArray->desc.y+cuArray->desc.z)/8; + tex_array_index = tex_array_base + x; + + break; + case GEOM_MODIFIER_2D: + width = cuArray->width; + height = cuArray->height; + if (texref->normalized) { + x_f32 = reduce_precision(ptx_tex_regs[0].f32,16); + y_f32 = reduce_precision(ptx_tex_regs[1].f32,15); + + if (texref->addressMode[0]) {//clamp + if (x_f32<0) x_f32 = 0; + if (x_f32>=1) x_f32 = 1 - 1/x_f32; + } else {//wrap + x_f32 = x_f32 - floor(x_f32); + } + if (texref->addressMode[1]) {//clamp + if (y_f32<0) y_f32 = 0; + if (y_f32>=1) y_f32 = 1 - 1/y_f32; + } else {//wrap + y_f32 = y_f32 - floor(y_f32); + } + + if( texref->filterMode == cudaFilterModeLinear ) { + float xb = x_f32 * width - 0.5; + float yb = y_f32 * height - 0.5; + alpha = xb - floor(xb); + beta = yb - floor(yb); + alpha = reduce_precision(alpha,9); + beta = reduce_precision(beta,9); + + x = (int)floor(xb); + y = (int)floor(yb); + } else { + x = (int) floor(x_f32 * width); + y = (int) floor(y_f32 * height); + } + } else { + x_f32 = ptx_tex_regs[0].f32; + y_f32 = ptx_tex_regs[1].f32; + + alpha = x_f32 - floor(x_f32); + beta = y_f32 - floor(y_f32); + + x = (int) x_f32; + y = (int) y_f32; + if (texref->addressMode[0]) {//clamp + if (x<0) x = 0; + if (x>= (int)width) x = width-1; + } else {//wrap + x = x % width; + if (x < 0) x*= -1; + } + if (texref->addressMode[1]) {//clamp + if (y<0) y = 0; + if (y>= (int)height) y = height -1; + } else {//wrap + y = y % height; + if (y < 0) y *= -1; + } + } + + width *= (cuArray->desc.w+cuArray->desc.x+cuArray->desc.y+cuArray->desc.z)/8; + x *= (cuArray->desc.w+cuArray->desc.x+cuArray->desc.y+cuArray->desc.z)/8; + tex_array_index = tex_array_base + (x + width*y); + break; + default: + assert(0); break; + } + switch ( to_type ) { + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + mem->read( tex_array_index, cuArray->desc.x/8, &data1.u32); + if (cuArray->desc.y) { + mem->read( tex_array_index+4, cuArray->desc.y/8, &data2.u32); + if (cuArray->desc.z) { + mem->read( tex_array_index+8, cuArray->desc.z/8, &data3.u32); + if (cuArray->desc.w) + mem->read( tex_array_index+12, cuArray->desc.w/8, &data4.u32); + } + } + break; + case U64_TYPE: + case S64_TYPE: + mem->read( tex_array_index, 8, &data1.u64); + if (cuArray->desc.y) { + mem->read( tex_array_index+4, 8, &data2.u64); + if (cuArray->desc.z) { + mem->read( tex_array_index+8, 8, &data3.u64); + if (cuArray->desc.w) + mem->read( tex_array_index+12, 8, &data4.u64); + } + } + break; + case F16_TYPE: assert(0); break; + case F32_TYPE: { + if( texref->filterMode == cudaFilterModeLinear ) { + float Tij; + float Ti1j; + float Tij1; + float Ti1j1; + + mem->read(tex_array_base + wrap(x,y,width,height), 4, &Tij); + mem->read(tex_array_base + wrap(x+4,y,width,height), 4, &Ti1j); + mem->read(tex_array_base + wrap(x,y+1,width,height), 4, &Tij1); + mem->read(tex_array_base + wrap(x+4,y+1,width,height), 4, &Ti1j1); + + data1.f32 = (1-alpha)*(1-beta)*Tij + + alpha*(1-beta)*Ti1j + + (1-alpha)*beta*Tij1 + + alpha*beta*Ti1j1; + + } else { + mem->read( tex_array_index, cuArray->desc.x/8, &data1.f32); + if (cuArray->desc.y) { + mem->read( tex_array_index+4, cuArray->desc.y/8, &data2.f32); + if (cuArray->desc.z) { + mem->read( tex_array_index+8, cuArray->desc.z/8, &data3.f32); + if (cuArray->desc.w) + mem->read( tex_array_index+12, cuArray->desc.w/8, &data4.f32); + } + } + } + } break; + case F64_TYPE: + mem->read( tex_array_index, 8, &data1.f64); + if (cuArray->desc.y) { + mem->read( tex_array_index+8, 8, &data2.f64); + if (cuArray->desc.z) { + mem->read( tex_array_index+16, 8, &data3.f64); + if (cuArray->desc.w) + mem->read( tex_array_index+24, 8, &data4.f64); + } + } + break; + default: assert(0); break; + } + int x_block_coord, y_block_coord, memreqindex, blockoffset; + + switch (dimension) { + case GEOM_MODIFIER_1D: + thread->m_last_effective_address = tex_array_index; + break; + case GEOM_MODIFIER_2D: + x_block_coord = x; + x_block_coord = x_block_coord >> (texInfo->Tx_numbits + texInfo->texel_size_numbits); + + y_block_coord = y; + y_block_coord = y_block_coord >> texInfo->Ty_numbits; + + memreqindex = ((y_block_coord*cuArray->width/texInfo->Tx)+x_block_coord)<<6; + + blockoffset = (x%(texInfo->Tx*texInfo->texel_size) + (y%(texInfo->Ty)<<(texInfo->Tx_numbits + texInfo->texel_size_numbits))); + memreqindex += blockoffset; + thread->m_last_effective_address = tex_array_base + memreqindex;//tex_array_index; + break; + default: + assert(0); + } + thread->m_last_memory_space = TEX_DIRECTIVE; + thread->set_vector_operand_values(dst,data1,data2,data3,data4,4); +} +void trap_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +extern unsigned g_warp_active_mask; + +void vote_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + static bool first_in_warp = true; + static bool and_all; + static bool or_all; + static std::list<ptx_thread_info*> threads_in_warp; + static unsigned last_tid; + + if( first_in_warp ) { + first_in_warp = false; + threads_in_warp.clear(); + and_all = true; + or_all = false; + unsigned mask=0x80000000; + unsigned offset=31; + while( mask && ((mask & g_warp_active_mask)==0) ) { + mask = mask>>1; + offset--; + } + last_tid = (thread->get_hw_tid() - (thread->get_hw_tid()%pI->warp_size())) + offset; + } + + ptx_reg_t src1_data; + const operand_info &src1 = pI->src1(); + src1_data = thread->get_operand_value(src1); + + bool pred_value = src1_data.pred; + bool invert = src1.is_neg_pred(); + + threads_in_warp.push_back(thread); + and_all &= (invert ^ pred_value); + or_all |= (invert ^ pred_value); + + // TODO: determine last active thread in warp... + if( thread->get_hw_tid() == last_tid ) { + bool pred_value = false; + + switch( pI->vote_mode() ) { + case ptx_instruction::vote_any: pred_value = or_all; break; + case ptx_instruction::vote_all: pred_value = and_all; break; + case ptx_instruction::vote_uni: pred_value = (or_all ^ and_all); break; + default: + abort(); + } + ptx_reg_t data; + data.pred = pred_value?1:0; + + for( std::list<ptx_thread_info*>::iterator t=threads_in_warp.begin(); t!=threads_in_warp.end(); ++t ) { + const operand_info &dst = pI->dst(); + (*t)->set_operand_value(dst,data); + } + first_in_warp = true; + } +} + +void xor_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 ^ src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void inst_not_implemented( const ptx_instruction * pI ) +{ + printf("Execution error (%s:%u): instruction \"%s\" not (yet) implemented\n", + pI->source_file(), + pI->source_line(), + pI->get_opcode_cstr() ); + abort(); +} + +void print_instruction(const ptx_instruction *instruction) +{ +} + diff --git a/src/cuda-sim/memory.cc b/src/cuda-sim/memory.cc new file mode 100644 index 0000000..c6a1a9b --- /dev/null +++ b/src/cuda-sim/memory.cc @@ -0,0 +1,154 @@ +/* + * memory.cc + * + * Copyright © 2009 by Tor M. Aamodt, Wilson W. L. Fung and the University of + * British Columbia, Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "memory.h" +#include <stdlib.h> + +template<unsigned BSIZE> memory_space_impl<BSIZE>::memory_space_impl( std::string name, unsigned hash_size ) +{ + m_name = name; + MEM_MAP_RESIZE(hash_size); + + m_log2_block_size = -1; + for( unsigned n=0, mask=1; mask != 0; mask <<= 1, n++ ) { + if( BSIZE & mask ) { + assert( m_log2_block_size == (unsigned)-1 ); + m_log2_block_size = n; + } + } + assert( m_log2_block_size != (unsigned)-1 ); +} + +template<unsigned BSIZE> void memory_space_impl<BSIZE>::write( mem_addr_t addr, size_t length, const void *data ) +{ + mem_addr_t index = addr >> m_log2_block_size; + unsigned offset = addr & (BSIZE-1); + unsigned nbytes = length; + assert( (addr+length) <= (index+1)*BSIZE ); + m_data[index].write(offset,nbytes,(const unsigned char*)data); +} + +template<unsigned BSIZE> void memory_space_impl<BSIZE>::read( mem_addr_t addr, size_t length, void *data ) const +{ + mem_addr_t index = addr >> m_log2_block_size; + unsigned offset = addr & (BSIZE-1); + unsigned nbytes = length; + typename map_t::const_iterator i = m_data.find(index); + assert( (addr+length) <= (index+1)*BSIZE ); + if( i == m_data.end() ) { + for( size_t n=0; n < length; n++ ) + ((unsigned char*)data)[n] = (unsigned char) 0; + //printf("GPGPU-Sim PTX: WARNING reading %zu bytes from unititialized memory at address 0x%x in space %s\n", length, addr, m_name.c_str() ); + } else { + i->second.read(offset,nbytes,(unsigned char*)data); + } +} + +template class memory_space_impl<32>; +template class memory_space_impl<64>; +template class memory_space_impl<8192>; +template class memory_space_impl<16*1024>; + + +#ifdef UNIT_TEST + +int main(int argc, char *argv[] ) +{ + int errors_found=0; + memory_space *mem = new memory_space_impl<32>("test",4); + // write address to [address] + for( mem_addr_t addr=0; addr < 16*1024; addr+=4) + mem->write(addr,4,&addr); + + for( mem_addr_t addr=0; addr < 16*1024; addr+=4) { + unsigned tmp=0; + mem->read(addr,4,&tmp); + if( tmp != addr ) { + errors_found=1; + printf("ERROR ** mem[0x%x] = 0x%x, expected 0x%x\n", addr, tmp, addr ); + } + } + + for( mem_addr_t addr=0; addr < 16*1024; addr+=1) { + unsigned char val = (addr + 128) % 256; + mem->write(addr,1,&val); + } + + for( mem_addr_t addr=0; addr < 16*1024; addr+=1) { + unsigned tmp=0; + mem->read(addr,1,&tmp); + unsigned char val = (addr + 128) % 256; + if( tmp != val ) { + errors_found=1; + printf("ERROR ** mem[0x%x] = 0x%x, expected 0x%x\n", addr, tmp, (unsigned)val ); + } + } + + if( errors_found ) { + printf("SUMMARY: ERRORS FOUND\n"); + } else { + printf("SUMMARY: UNIT TEST PASSED\n"); + } +} + +#endif diff --git a/src/cuda-sim/memory.h b/src/cuda-sim/memory.h new file mode 100644 index 0000000..6eebcf4 --- /dev/null +++ b/src/cuda-sim/memory.h @@ -0,0 +1,148 @@ +/* + * Copyright © 2009 by Tor M. Aamodt, Wilson W. L. Fung and the University of + * British Columbia, Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef memory_h_INCLUDED +#define memory_h_INCLUDED + +#ifdef __GNUC__ +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 + #include <unordered_map> + #define mem_map std::unordered_map + #define MEM_MAP_RESIZE(hash_size) m_data.rehash(hash_size) +#else + #include <ext/hash_map> + namespace std { + using namespace __gnu_cxx; + } + #define mem_map std::hash_map + #define MEM_MAP_RESIZE(hash_size) m_data.resize(hash_size) +#endif +#else + #include <map> + #define mem_map std::map + #define MEM_MAP_RESIZE(hash_size) +#endif + +#include <assert.h> +#include <string.h> +#include <string> +#include "../util.h" + +typedef address_type mem_addr_t; + +#define MEM_BLOCK_SIZE (4*1024) + +template<unsigned BSIZE> class mem_storage { +public: + mem_storage( const mem_storage &another ) + { + m_data = new unsigned char[ BSIZE ]; + memcpy(m_data,another.m_data,BSIZE); + } + mem_storage() + { + m_data = new unsigned char[ BSIZE ]; + } + ~mem_storage() + { + delete[] m_data; + } + + void write( unsigned offset, size_t length, const unsigned char *data ) + { + assert( offset + length <= BSIZE ); + memcpy(m_data+offset,data,length); + } + + void read( unsigned offset, size_t length, unsigned char *data ) const + { + assert( offset + length <= BSIZE ); + memcpy(data,m_data+offset,length); + } + +private: + unsigned m_nbytes; + unsigned char *m_data; +}; + +class memory_space +{ +public: + virtual ~memory_space() {} + virtual void write( mem_addr_t addr, size_t length, const void *data ) = 0; + virtual void read( mem_addr_t addr, size_t length, void *data ) const = 0;; +}; + +template<unsigned BSIZE> class memory_space_impl : public memory_space { +public: + memory_space_impl( std::string name, unsigned hash_size ); + + virtual void write( mem_addr_t addr, size_t length, const void *data ); + virtual void read( mem_addr_t addr, size_t length, void *data ) const; + +private: + std::string m_name; + unsigned m_log2_block_size; + typedef mem_map<mem_addr_t,mem_storage<BSIZE> > map_t; + map_t m_data; +}; + +#endif diff --git a/src/cuda-sim/opcodes.def b/src/cuda-sim/opcodes.def new file mode 100644 index 0000000..ba6657f --- /dev/null +++ b/src/cuda-sim/opcodes.def @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +/*6th operand of each OP_DEF reflects its classification */ +/*Type +ALU 1 +MAD 2 +Control 3 +SFU 4 +Mem(except Tex) 5 +Tex 6 +*/ +OP_DEF(ABS_OP,abs_impl,"abs",1,1) +OP_DEF(ADD_OP,add_impl,"add",1,1) +OP_DEF(AND_OP,and_impl,"and",1,1) +OP_DEF(ATOM_OP,atom_impl,"atom",0,3) +OP_DEF(BAR_OP,bar_sync_impl,"bar.sync",1,3) +OP_DEF(BRA_OP,bra_impl,"bra",0,3) +OP_DEF(CALL_OP,call_impl,"call",1,3) /*call may actually return an value if the syntax is call (ret-param),fname,(param-list)*/ +OP_DEF(CNOT_OP,cnot_impl,"cnot",1,1) +OP_DEF(COS_OP,cos_impl,"cos",1,4) +OP_DEF(CVT_OP,cvt_impl,"cvt",1,1) +OP_DEF(DIV_OP,div_impl,"div",1,1) +OP_DEF(EX2_OP,ex2_impl,"ex2",1,4) +OP_DEF(EXIT_OP,exit_impl,"exit",1,3) +OP_DEF(LD_OP,ld_impl,"ld",1,5) +OP_DEF(LG2_OP,lg2_impl,"lg2",1,4) +OP_DEF(MAD24_OP,mad24_impl,"mad24",1,2) +OP_DEF(MAD_OP,mad_impl,"mad",1,2) +OP_DEF(MAX_OP,max_impl,"max",1,1) +OP_DEF(MIN_OP,min_impl,"min",1,1) +OP_DEF(MOV_OP,mov_impl,"mov",1,1) +OP_DEF(MUL24_OP,mul24_impl,"mul24",1,1) +OP_DEF(MUL_OP,mul_impl,"mul",1,1) +OP_DEF(NEG_OP,neg_impl,"neg",1,1) +OP_DEF(NOT_OP,not_impl,"not",1,1) +OP_DEF(OR_OP,or_impl,"or",1,1) +OP_DEF(RCP_OP,rcp_impl,"rcp",1,4) +OP_DEF(REM_OP,rem_impl,"rem",1,1) +OP_DEF(RET_OP,ret_impl,"ret",0,3) +OP_DEF(RSQRT_OP,rsqrt_impl,"rsqrt",1,4) +OP_DEF(SAD_OP,sad_impl,"sad",1,1) +OP_DEF(SELP_OP,selp_impl,"selp",1,1) +OP_DEF(SETP_OP,setp_impl,"setp",1,1) +OP_DEF(SET_OP,set_impl,"set",1,1) +OP_DEF(SHL_OP,shl_impl,"shl",1,1) +OP_DEF(SHR_OP,shr_impl,"shr",1,1) +OP_DEF(SIN_OP,sin_impl,"sin",1,4) +OP_DEF(SLCT_OP,slct_impl,"slct",1,1) +OP_DEF(SQRT_OP,sqrt_impl,"sqrt",1,4) +OP_DEF(ST_OP,st_impl,"st",0,5) +OP_DEF(SUB_OP,sub_impl,"sub",1,1) +OP_DEF(TEX_OP,tex_impl,"tex",1,6) +OP_DEF(TRAP_OP,trap_impl,"trap",1,3) +OP_DEF(VOTE_OP,vote_impl,"vote",0,3) +OP_DEF(XOR_OP,xor_impl,"xor",1,1) +OP_DEF(MEMBAR_OP,membar_impl,"membar",1,3) +OP_DEF(RED_OP,red_impl,"red",1,7) +OP_DEF(ADDC_OP,addc_impl,"addc",1,1) +OP_DEF(BRKPT_OP,brkpt_impl,"brkpt",1,9) diff --git a/src/cuda-sim/opcodes.h b/src/cuda-sim/opcodes.h new file mode 100644 index 0000000..221bf2e --- /dev/null +++ b/src/cuda-sim/opcodes.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef opcodes_h_included +#define opcodes_h_included + +enum opcode_t { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) OP, +#include "opcodes.def" + NUM_OPCODES +#undef OP_DEF +}; + +enum special_regs { + CLOCK_ID, + CTA_ID, + NTID_ID, + GRIDID_ID, + NCTAID_ID, + TID_ID +}; + +#endif diff --git a/src/cuda-sim/ptx-stats.cc b/src/cuda-sim/ptx-stats.cc new file mode 100644 index 0000000..2d18286 --- /dev/null +++ b/src/cuda-sim/ptx-stats.cc @@ -0,0 +1,298 @@ +/* + * Copyright © 2009 by Wilson W. L. Fung and the University of British Columbia, + * Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "ptx_ir.h" +#include "ptx_sim.h" +#include "ptx-stats.h" +#include "../option_parser.h" +#include <stdio.h> +#include <map> + +// external dependencies +extern function_info *g_func_info; + +// options +int enable_ptx_file_line_stats; +char * ptx_line_stats_filename = NULL; + +void ptx_file_line_stats_options(option_parser_t opp) +{ + option_parser_register(opp, "-enable_ptx_file_line_stats", OPT_BOOL, + &enable_ptx_file_line_stats, + "Turn on PTX source line statistic profiling. (1 = On)", "1"); + option_parser_register(opp, "-ptx_line_stats_filename", OPT_CSTR, + &ptx_line_stats_filename, + "Output file for PTX source line statistics.", "gpgpu_inst_stats.txt"); +} + +// implementations + +// defining a PTX source line = filename + line number +class ptx_file_line +{ +public: + ptx_file_line(const char* s, int l){ + if( s == NULL ) + st = "NULL_NAME"; + else + st = s; + line = l; + } + + bool operator<(const ptx_file_line &other) const { + if( st == other.st ) { + if( line < other.line ) + return true; + else + return false; + } else { + return st < other.st; + } + } + + bool operator==(const ptx_file_line &other) const { + return (st==other.st) and (line==other.line); + } + + std::string st; + unsigned line; +}; + +// holds all statistics collected for a singe PTX source line +class ptx_file_line_stats +{ +public: + ptx_file_line_stats() + : exec_count(0), latency(0), dram_traffic(0), + smem_n_way_bank_conflict_total(0), smem_warp_count(0), + gmem_n_access_total(0), gmem_warp_count(0), exposed_latency(0), + warp_divergence(0) + { } + + unsigned long exec_count; + unsigned long long latency; + unsigned long long dram_traffic; + unsigned long long smem_n_way_bank_conflict_total; // total number of banks accessed by this instruction + unsigned long smem_warp_count; // number of warps accessing shared memory + unsigned long long gmem_n_access_total; // number of uncoalesced access in total from this instruction + unsigned long gmem_warp_count; // number of warps causing these uncoalesced access + unsigned long long exposed_latency; // latency exposed as pipeline bubbles (attributed to this instruction) + unsigned long long warp_divergence; // number of warp divergence occured at this instruction +}; + +static std::map<ptx_file_line, ptx_file_line_stats> ptx_file_line_stats_tracker; + +// output statistics to a file +void ptx_file_line_stats_write_file() +{ + // check if stat collection is turned on + if (enable_ptx_file_line_stats == 0) return; + + std::map<ptx_file_line, ptx_file_line_stats>::iterator it; + FILE * pfile; + + pfile = fopen(ptx_line_stats_filename, "w"); + fprintf(pfile,"kernel line : count latency dram_traffic smem_bk_conflicts smem_warp gmem_access_generated gmem_warp exposed_latency warp_divergence\n"); + for( it=ptx_file_line_stats_tracker.begin(); it != ptx_file_line_stats_tracker.end(); it++ ) { + fprintf(pfile, "%s %i : ", it->first.st.c_str(), it->first.line); + fprintf(pfile, "%lu ", it->second.exec_count); + fprintf(pfile, "%llu ", it->second.latency); + fprintf(pfile, "%llu ", it->second.dram_traffic); + fprintf(pfile, "%llu ", it->second.smem_n_way_bank_conflict_total); + fprintf(pfile, "%lu ", it->second.smem_warp_count); + fprintf(pfile, "%llu ", it->second.gmem_n_access_total); + fprintf(pfile, "%lu ", it->second.gmem_warp_count); + fprintf(pfile, "%llu ", it->second.exposed_latency); + fprintf(pfile, "%llu ", it->second.warp_divergence); + fprintf(pfile, "\n"); + } + fflush(pfile); + fclose(pfile); +} + +// attribute one more execution count to this ptx instruction +// counting the number of threads (not warps) executing this instruction +void ptx_file_line_stats_add_exec_count(const ptx_instruction *pInsn) +{ + ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())].exec_count += 1; +} + +// attribute pipeline latency to this ptx instruction (specified by the pc) +// pipeline latency is the number of cycles a warp with this instruction spent in the pipeline +void ptx_file_line_stats_add_latency(void * ptx_thd, unsigned pc, unsigned latency) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())].latency += latency; +} + +// attribute dram traffic to this ptx instruction (specified by the pc) +// dram traffic is counted in number of requests +void ptx_file_line_stats_add_dram_traffic(unsigned pc, unsigned dram_traffic) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())].dram_traffic += dram_traffic; +} + +// attribute the number of shared memory access cycles to a ptx instruction +// counts both the number of warps doing shared memory access and the number of cycles involved +void ptx_file_line_stats_add_smem_bank_conflict(void * ptx_thd, unsigned pc, unsigned n_way_bkconflict) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.smem_n_way_bank_conflict_total += n_way_bkconflict; + line_stats.smem_warp_count += 1; +} + +// attribute a non-coalesced mem access to a ptx instruction +// counts both the number of warps causing this and the number of memory requests generated +void ptx_file_line_stats_add_uncoalesced_gmem(void * ptx_thd, unsigned pc, unsigned n_access) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.gmem_n_access_total += n_access; + line_stats.gmem_warp_count += 1; +} + +// a class that tracks the inflight memory instructions of a shader core +// and attributes exposed latency to those instructions when signaled to do so +class ptx_inflight_memory_insn_tracker +{ +public: + typedef std::map<const ptx_instruction *, int> insn_count_map; + + void add_count(const ptx_instruction * pInsn, int count = 1) + { + ptx_inflight_memory_insns[pInsn] += count; + } + + void sub_count(const ptx_instruction * pInsn, int count = 1) + { + insn_count_map::iterator i_insncount; + i_insncount = ptx_inflight_memory_insns.find(pInsn); + + assert(i_insncount != ptx_inflight_memory_insns.end()); + + i_insncount->second -= count; + + if (i_insncount->second <= 0) { + ptx_inflight_memory_insns.erase(i_insncount); + } + } + + void attribute_exposed_latency(int count = 1) + { + insn_count_map &exlat_insnmap = ptx_inflight_memory_insns; + insn_count_map::const_iterator i_exlatinsn; + + i_exlatinsn = exlat_insnmap.begin(); + for (; i_exlatinsn != exlat_insnmap.end(); ++i_exlatinsn) { + const ptx_instruction *pInsn = i_exlatinsn->first; + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.exposed_latency += count; + } + } + + insn_count_map ptx_inflight_memory_insns; +}; + +static ptx_inflight_memory_insn_tracker *inflight_mem_tracker = NULL; + +void ptx_file_line_stats_create_exposed_latency_tracker(int n_shader_cores) +{ + inflight_mem_tracker = new ptx_inflight_memory_insn_tracker[n_shader_cores]; +} + +// add an inflight memory instruction +void ptx_file_line_stats_add_inflight_memory_insn(int sc_id, unsigned pc) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + inflight_mem_tracker[sc_id].add_count(pInsn); +} + +// remove an inflight memory instruction +void ptx_file_line_stats_sub_inflight_memory_insn(int sc_id, unsigned pc) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + inflight_mem_tracker[sc_id].sub_count(pInsn); +} + +// attribute an empty cycle in the pipeline (exposed latency) to the ptx memory instructions in flight +void ptx_file_line_stats_commit_exposed_latency(int sc_id, int exposed_latency) +{ + assert(exposed_latency > 0); + inflight_mem_tracker[sc_id].attribute_exposed_latency(exposed_latency); +} + +// attribute the number of warp divergence to a ptx instruction +void ptx_file_line_stats_add_warp_divergence(unsigned pc, unsigned n_way_divergence) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.warp_divergence += n_way_divergence; +} + diff --git a/src/cuda-sim/ptx-stats.h b/src/cuda-sim/ptx-stats.h new file mode 100644 index 0000000..200f7a5 --- /dev/null +++ b/src/cuda-sim/ptx-stats.h @@ -0,0 +1,92 @@ +/* + * Copyright © 2009 by Wilson W. L. Fung and the University of British Columbia, + * Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#pragma once + +#include "../option_parser.h" + +extern int enable_ptx_file_line_stats; + +// set options +void ptx_file_line_stats_options(option_parser_t opp); + +// output stats to a file +void ptx_file_line_stats_write_file(); + +#ifdef __cplusplus +// stat collection interface to cuda-sim +class ptx_instruction; +void ptx_file_line_stats_add_exec_count(const ptx_instruction *pInsn); +#endif + +// stat collection interface to gpgpu-sim +void ptx_file_line_stats_add_latency(void * ptx_thd, unsigned pc, unsigned latency); +void ptx_file_line_stats_add_dram_traffic(unsigned pc, unsigned dram_traffic); +void ptx_file_line_stats_add_smem_bank_conflict(void * ptx_thd, unsigned pc, unsigned n_way_bkconflict); +void ptx_file_line_stats_add_uncoalesced_gmem(void * ptx_thd, unsigned pc, unsigned n_access); + +void ptx_file_line_stats_create_exposed_latency_tracker(int n_shader_cores); +void ptx_file_line_stats_add_inflight_memory_insn(int sc_id, unsigned pc); +void ptx_file_line_stats_sub_inflight_memory_insn(int sc_id, unsigned pc); +void ptx_file_line_stats_commit_exposed_latency(int sc_id, int exposed_latency); + +void ptx_file_line_stats_add_warp_divergence(unsigned pc, unsigned n_way_divergence); + diff --git a/src/cuda-sim/ptx.l b/src/cuda-sim/ptx.l new file mode 100644 index 0000000..d764c7f --- /dev/null +++ b/src/cuda-sim/ptx.l @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%option noyywrap +%option yylineno +%option prefix="ptx_" +%{ +#include "opcodes.h" +#include "ptx.tab.h" +#include <string.h> + +char linebuf[1024]; +unsigned col = 0; +#define TC col+=strlen(ptx_text); +int ptx_error( const char *s ); +%} + +%s IN_STRING +%s IN_COMMENT +%x NOT_OPCODE +%% + +abs TC; ptx_lval.int_value = ABS_OP; return OPCODE; +add TC; ptx_lval.int_value = ADD_OP; return OPCODE; +and TC; ptx_lval.int_value = AND_OP; return OPCODE; +atom TC; ptx_lval.int_value = ATOM_OP; return OPCODE; +bar.sync TC; ptx_lval.int_value = BAR_OP; return OPCODE; +bra TC; ptx_lval.int_value = BRA_OP; return OPCODE; +call TC; ptx_lval.int_value = CALL_OP; return OPCODE; +cnot TC; ptx_lval.int_value = CNOT_OP; return OPCODE; +cos TC; ptx_lval.int_value = COS_OP; return OPCODE; +cvt TC; ptx_lval.int_value = CVT_OP; return OPCODE; +div TC; ptx_lval.int_value = DIV_OP; return OPCODE; +ex2 TC; ptx_lval.int_value = EX2_OP; return OPCODE; +exit TC; ptx_lval.int_value = EXIT_OP; return OPCODE; +ld TC; ptx_lval.int_value = LD_OP; return OPCODE; +ld.volatile TC; ptx_lval.int_value = LD_OP; return OPCODE; +lg2 TC; ptx_lval.int_value = LG2_OP; return OPCODE; +mad24 TC; ptx_lval.int_value = MAD24_OP; return OPCODE; +mad TC; ptx_lval.int_value = MAD_OP; return OPCODE; +max TC; ptx_lval.int_value = MAX_OP; return OPCODE; +min TC; ptx_lval.int_value = MIN_OP; return OPCODE; +mov TC; ptx_lval.int_value = MOV_OP; return OPCODE; +mul24 TC; ptx_lval.int_value = MUL24_OP; return OPCODE; +mul TC; ptx_lval.int_value = MUL_OP; return OPCODE; +neg TC; ptx_lval.int_value = NEG_OP; return OPCODE; +not TC; ptx_lval.int_value = NOT_OP; return OPCODE; +or TC; ptx_lval.int_value = OR_OP; return OPCODE; +rcp TC; ptx_lval.int_value = RCP_OP; return OPCODE; +rem TC; ptx_lval.int_value = REM_OP; return OPCODE; +ret TC; ptx_lval.int_value = RET_OP; return OPCODE; +rsqrt TC; ptx_lval.int_value = RSQRT_OP; return OPCODE; +sad TC; ptx_lval.int_value = SAD_OP; return OPCODE; +selp TC; ptx_lval.int_value = SELP_OP; return OPCODE; +setp TC; ptx_lval.int_value = SETP_OP; return OPCODE; +set TC; ptx_lval.int_value = SET_OP; return OPCODE; +shl TC; ptx_lval.int_value = SHL_OP; return OPCODE; +shr TC; ptx_lval.int_value = SHR_OP; return OPCODE; +sin TC; ptx_lval.int_value = SIN_OP; return OPCODE; +slct TC; ptx_lval.int_value = SLCT_OP; return OPCODE; +sqrt TC; ptx_lval.int_value = SQRT_OP; return OPCODE; +st TC; ptx_lval.int_value = ST_OP; return OPCODE; +st.volatile TC; ptx_lval.int_value = ST_OP; return OPCODE; +sub TC; ptx_lval.int_value = SUB_OP; return OPCODE; +tex TC; BEGIN(NOT_OPCODE); ptx_lval.int_value = TEX_OP; return OPCODE; +trap TC; ptx_lval.int_value = TRAP_OP; return OPCODE; +vote TC; ptx_lval.int_value = VOTE_OP; return OPCODE; +xor TC; ptx_lval.int_value = XOR_OP; return OPCODE; +membar TC; ptx_lval.int_value = MEMBAR_OP; return OPCODE; +red TC; ptx_lval.int_value = RED_OP; return OPCODE; +brkpt TC; ptx_lval.int_value = BRKPT_OP; return OPCODE; +addc TC; ptx_lval.int_value = ADDC_OP; return OPCODE; + +<INITIAL,NOT_OPCODE>{ + +\.align TC; return ALIGN_DIRECTIVE; +\.byte TC; return BYTE_DIRECTIVE; +\.const\[[0-9]+\] TC; return CONST_DIRECTIVE; +\.const TC; return CONST_DIRECTIVE; +\.entry TC; return ENTRY_DIRECTIVE; +\.extern TC; return EXTERN_DIRECTIVE; +\.file TC; return FILE_DIRECTIVE; +\.func TC; return FUNC_DIRECTIVE; +\.global TC; return GLOBAL_DIRECTIVE; +\.local TC; return LOCAL_DIRECTIVE; +\.loc TC; return LOC_DIRECTIVE; +\.param TC; return PARAM_DIRECTIVE; +\.reg TC; return REG_DIRECTIVE; +\.section TC; return SECTION_DIRECTIVE; +\.shared TC; return SHARED_DIRECTIVE; +\.sreg TC; return SREG_DIRECTIVE; +\.struct TC; return STRUCT_DIRECTIVE; +\.surf TC; return SURF_DIRECTIVE; +\.target TC; return TARGET_DIRECTIVE; +\.tex TC; BEGIN(NOT_OPCODE); return TEX_DIRECTIVE; +\.union TC; return UNION_DIRECTIVE; +\.version TC; return VERSION_DIRECTIVE; +\.visible TC; return VISIBLE_DIRECTIVE; + +\.maxntid TC; return MAXNTID_DIRECTIVE; + +"%clock" TC; ptx_lval.int_value = CLOCK_ID; return SPECIAL_REGISTER; +"%ctaid" TC; ptx_lval.int_value = CTA_ID; return SPECIAL_REGISTER; +"%ntid" TC; ptx_lval.int_value = NTID_ID; return SPECIAL_REGISTER; +"%gridid" TC; ptx_lval.int_value = GRIDID_ID; return SPECIAL_REGISTER; +"%nctaid" TC; ptx_lval.int_value = NCTAID_ID; return SPECIAL_REGISTER; +"%tid" TC; ptx_lval.int_value = TID_ID; return SPECIAL_REGISTER; + +[_A-Za-z$%][_0-9A-Za-z$]* TC; ptx_lval.string_value = strdup(yytext); /*printf("\n<<identifier=\"%s\">>\n", yytext); fflush(stdout);*/ return IDENTIFIER; + +[0-9]+\.[0-9]+ TC; sscanf(yytext,"%lf", &ptx_lval.double_value); return DOUBLE_OPERAND; +[-]{0,1}[0-9]+ TC; ptx_lval.int_value = atoi(yytext); return INT_OPERAND; +0[xX][0-9]+ TC; sscanf(yytext,"%x", &ptx_lval.int_value); return INT_OPERAND; +0[fF][0-9a-fA-F]{8} TC; sscanf(yytext+2,"%x", (unsigned*)(void*)&ptx_lval.float_value); return FLOAT_OPERAND; +0[dD][0-9a-fA-F]{16} TC; sscanf(yytext+2,"%Lx", (unsigned long long*)(void*)&ptx_lval.double_value); return DOUBLE_OPERAND; + +\.s8 TC; return S8_TYPE; +\.s16 TC; return S16_TYPE; +\.s32 TC; return S32_TYPE; +\.s64 TC; return S64_TYPE; +\.u8 TC; return U8_TYPE; +\.u16 TC; return U16_TYPE; +\.u32 TC; return U32_TYPE; +\.u64 TC; return U64_TYPE; +\.f16 TC; return F16_TYPE; +\.f32 TC; return F32_TYPE; +\.f64 TC; return F64_TYPE; +\.b8 TC; return B8_TYPE; +\.b16 TC; return B16_TYPE; +\.b32 TC; return B32_TYPE; +\.b64 TC; return B64_TYPE; +\.pred TC; return PRED_TYPE; + +\.v2 TC; return V2_TYPE; +\.v3 TC; return V3_TYPE; +\.v4 TC; return V4_TYPE; + +\.equ TC; return EQU_OPTION; +\.neu TC; return NEU_OPTION; +\.ltu TC; return LTU_OPTION; +\.leu TC; return LEU_OPTION; +\.gtu TC; return GTU_OPTION; +\.geu TC; return GEU_OPTION; +\.num TC; return NUM_OPTION; +\.nan TC; return NAN_OPTION; + +\.sat TC; return SAT_OPTION; + +\.eq TC; return EQ_OPTION; +\.ne TC; return NE_OPTION; +\.lt TC; return LT_OPTION; +\.le TC; return LE_OPTION; +\.gt TC; return GT_OPTION; +\.ge TC; return GE_OPTION; + +\.lo TC; return LO_OPTION; +\.ls TC; return LS_OPTION; +\.hi TC; return HI_OPTION; +\.hs TC; return HS_OPTION; + + +\.rni TC; return RNI_OPTION; +\.rzi TC; return RZI_OPTION; +\.rmi TC; return RMI_OPTION; +\.rpi TC; return RPI_OPTION; + +\.rn TC; return RN_OPTION; +\.rz TC; return RZ_OPTION; +\.rm TC; return RM_OPTION; +\.rp TC; return RP_OPTION; + +\.ftz TC; return FTZ_OPTION; + +\.wide TC; return WIDE_OPTION; +\.uni TC; return UNI_OPTION; + +\.approx TC; return APPROX_OPTION; +\.full TC; return FULL_OPTION; + +\.any TC; return ANY_OPTION; +\.all TC; return ALL_OPTION; +\.gl TC; return GLOBAL_OPTION; +\.cta TC; return CTA_OPTION; + +\.and TC; return ATOMIC_AND; +\.or TC; return ATOMIC_OR; +\.xor TC; return ATOMIC_XOR; +\.cas TC; return ATOMIC_CAS; +\.exch TC; return ATOMIC_EXCH; +\.add TC; return ATOMIC_ADD; +\.inc TC; return ATOMIC_INC; +\.dec TC; return ATOMIC_DEC; +\.min TC; return ATOMIC_MIN; +\.max TC; return ATOMIC_MAX; + +\.1d TC; return GEOM_MODIFIER_1D; +\.2d TC; return GEOM_MODIFIER_2D; +\.3d TC; return GEOM_MODIFIER_3D; + +\.0 TC; ptx_lval.int_value = 0; return DIMENSION_MODIFIER; +\.1 TC; ptx_lval.int_value = 1; return DIMENSION_MODIFIER; +\.2 TC; ptx_lval.int_value = 2; return DIMENSION_MODIFIER; +\.x TC; ptx_lval.int_value = 0; return DIMENSION_MODIFIER; +\.y TC; ptx_lval.int_value = 1; return DIMENSION_MODIFIER; +\.z TC; ptx_lval.int_value = 2; return DIMENSION_MODIFIER; + +"+" TC; return PLUS; +"," TC; return COMMA; +"@" TC; return PRED; +"[" TC; return LEFT_SQUARE_BRACKET; +"]" TC; return RIGHT_SQUARE_BRACKET; +"<" TC; return LEFT_ANGLE_BRACKET; +">" TC; return RIGHT_ANGLE_BRACKET; +"(" TC; return LEFT_PAREN; +")" TC; return RIGHT_PAREN; +":" TC; return COLON; +";" TC; BEGIN(INITIAL); return SEMI_COLON; +"!" TC; return EXCLAMATION; +"=" TC; return EQUALS; +"{" TC; return LEFT_BRACE; +"}" TC; return RIGHT_BRACE; +\. TC; return PERIOD; + +"//"[^\n]* TC; // eat single + +\n.* col=0; strncpy(linebuf, yytext + 1, 1024); yyless( 1 ); + +" " TC; +"\t" TC; + + +} + +<INITIAL>{ +"/*" BEGIN(IN_COMMENT); +} +<IN_COMMENT>{ +"*/" BEGIN(INITIAL); +[^*\n]+ // eat comment in chunks +"*" // eat the lone star +\n TC; +} + +<INITIAL>{ +"\"" BEGIN(IN_STRING); +} +<IN_STRING>{ +"\"" TC; BEGIN(INITIAL); return STRING; +[^\"]* TC; ptx_lval.string_value = strdup(yytext); +} + +<INITIAL,NOT_OPCODE>. TC; ptx_error((const char*)NULL); + +%% + +extern int g_error_detected; +extern const char *g_filename; + +int ptx_error( const char *s ) +{ + int i; + g_error_detected = 1; + fflush(stdout); + if( s != NULL ) + printf("%s:%u: Syntax error:\n\n", g_filename, ptx_lineno ); + printf(" %s\n", linebuf ); + printf(" "); + for( i=0; i < col-1; i++ ) { + if( linebuf[i] == '\t' ) printf("\t"); + else printf(" "); + } + + printf("^\n\n"); + fflush(stdout); + //exit(1); + return 0; +} diff --git a/src/cuda-sim/ptx.y b/src/cuda-sim/ptx.y new file mode 100644 index 0000000..2e1cb0f --- /dev/null +++ b/src/cuda-sim/ptx.y @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%union { + double double_value; + float float_value; + int int_value; + char * string_value; + void * ptr_value; +} + +%token <string_value> STRING +%token <int_value> OPCODE +%token ALIGN_DIRECTIVE +%token BYTE_DIRECTIVE +%token CONST_DIRECTIVE +%token ENTRY_DIRECTIVE +%token EXTERN_DIRECTIVE +%token FILE_DIRECTIVE +%token FUNC_DIRECTIVE +%token GLOBAL_DIRECTIVE +%token LOCAL_DIRECTIVE +%token LOC_DIRECTIVE +%token PARAM_DIRECTIVE +%token REG_DIRECTIVE +%token SECTION_DIRECTIVE +%token SHARED_DIRECTIVE +%token SREG_DIRECTIVE +%token STRUCT_DIRECTIVE +%token SURF_DIRECTIVE +%token TARGET_DIRECTIVE +%token TEX_DIRECTIVE +%token UNION_DIRECTIVE +%token VERSION_DIRECTIVE +%token VISIBLE_DIRECTIVE +%token MAXNTID_DIRECTIVE +%token <string_value> IDENTIFIER +%token <int_value> INT_OPERAND +%token <float_value> FLOAT_OPERAND +%token <double_value> DOUBLE_OPERAND +%token S8_TYPE +%token S16_TYPE +%token S32_TYPE +%token S64_TYPE +%token U8_TYPE +%token U16_TYPE +%token U32_TYPE +%token U64_TYPE +%token F16_TYPE +%token F32_TYPE +%token F64_TYPE +%token B8_TYPE +%token B16_TYPE +%token B32_TYPE +%token B64_TYPE +%token PRED_TYPE +%token V2_TYPE +%token V3_TYPE +%token V4_TYPE +%token COMMA +%token PRED +%token EQ_OPTION +%token NE_OPTION +%token LT_OPTION +%token LE_OPTION +%token GT_OPTION +%token GE_OPTION +%token LO_OPTION +%token LS_OPTION +%token HI_OPTION +%token HS_OPTION +%token EQU_OPTION +%token NEU_OPTION +%token LTU_OPTION +%token LEU_OPTION +%token GTU_OPTION +%token GEU_OPTION +%token NUM_OPTION +%token NAN_OPTION +%token LEFT_SQUARE_BRACKET +%token RIGHT_SQUARE_BRACKET +%token WIDE_OPTION +%token <int_value> SPECIAL_REGISTER +%token PLUS +%token COLON +%token SEMI_COLON +%token EXCLAMATION +%token RIGHT_BRACE +%token LEFT_BRACE +%token EQUALS +%token PERIOD +%token <int_value> DIMENSION_MODIFIER +%token RN_OPTION +%token RZ_OPTION +%token RM_OPTION +%token RP_OPTION +%token RNI_OPTION +%token RZI_OPTION +%token RMI_OPTION +%token RPI_OPTION +%token UNI_OPTION +%token GEOM_MODIFIER_1D +%token GEOM_MODIFIER_2D +%token GEOM_MODIFIER_3D +%token SAT_OPTION +%token FTZ_OPTION +%token ATOMIC_AND +%token ATOMIC_OR +%token ATOMIC_XOR +%token ATOMIC_CAS +%token ATOMIC_EXCH +%token ATOMIC_ADD +%token ATOMIC_INC +%token ATOMIC_DEC +%token ATOMIC_MIN +%token ATOMIC_MAX +%token LEFT_ANGLE_BRACKET +%token RIGHT_ANGLE_BRACKET +%token LEFT_PAREN +%token RIGHT_PAREN +%token APPROX_OPTION +%token FULL_OPTION +%token ANY_OPTION +%token ALL_OPTION +%token GLOBAL_OPTION +%token CTA_OPTION +%type <int_value> function_decl_header +%type <ptr_value> function_decl + +%{ + #include "ptx_ir.h" + #include <stdlib.h> + #include <string.h> + #include <math.h> + void syntax_not_implemented(); + extern int g_func_decl; + int ptx_lex(void); + int ptx_error(const char *); +%} + +%% + +input: /* empty */ + | input directive_statement + | input function_defn + | input function_decl + ; + +function_defn: function_decl { set_symtab($1); } LEFT_BRACE statement_list RIGHT_BRACE { end_function(); } + | function_decl { set_symtab($1); } block_spec LEFT_BRACE statement_list RIGHT_BRACE { end_function(); } + ; + +block_spec: MAXNTID_DIRECTIVE INT_OPERAND COMMA INT_OPERAND COMMA INT_OPERAND + ; + +function_decl: function_decl_header LEFT_PAREN { start_function($1); } param_entry RIGHT_PAREN function_ident_param { $$ = reset_symtab(); } + | function_decl_header { start_function($1); } function_ident_param { $$ = reset_symtab(); } + | function_decl_header { start_function($1); add_function_name(""); g_func_decl=0; $$ = reset_symtab(); } + ; + +function_ident_param: IDENTIFIER { add_function_name($1); } LEFT_PAREN param_list RIGHT_PAREN { g_func_decl=0; } + | IDENTIFIER { add_function_name($1); g_func_decl=0; } + ; + +function_decl_header: ENTRY_DIRECTIVE { $$ = 1; g_func_decl=1; } + | FUNC_DIRECTIVE { $$ = 0; g_func_decl=1; } + ; + +param_list: param_entry { add_directive(); } + | param_list COMMA param_entry { add_directive(); } + +param_entry: PARAM_DIRECTIVE { add_space_spec(PARAM_DIRECTIVE); } variable_spec identifier_spec { add_function_arg(); } + | REG_DIRECTIVE { add_space_spec(REG_DIRECTIVE); } variable_spec identifier_spec { add_function_arg(); } + +statement_list: directive_statement { add_directive(); } + | instruction_statement { add_instruction(); } + | statement_list directive_statement { add_directive(); } + | statement_list instruction_statement { add_instruction(); } + ; + +directive_statement: variable_declaration SEMI_COLON + | VERSION_DIRECTIVE DOUBLE_OPERAND + | TARGET_DIRECTIVE IDENTIFIER COMMA IDENTIFIER + | TARGET_DIRECTIVE IDENTIFIER + | FILE_DIRECTIVE INT_OPERAND STRING { add_file($2,$3); } + | LOC_DIRECTIVE INT_OPERAND INT_OPERAND INT_OPERAND + ; + +variable_declaration: variable_spec identifier_list { add_variables(); } + | variable_spec identifier_spec EQUALS initializer_list { add_variables(); } + | variable_spec identifier_spec EQUALS literal_operand { add_variables(); } + ; + +variable_spec: var_spec_list { set_variable_type(); } + +identifier_list: identifier_spec + | identifier_list COMMA identifier_spec; + +identifier_spec: IDENTIFIER { add_identifier($1,0,NON_ARRAY_IDENTIFIER); } + | IDENTIFIER LEFT_ANGLE_BRACKET INT_OPERAND RIGHT_ANGLE_BRACKET { + int i,lbase,l; + char *id = NULL; + lbase = strlen($1); + for( i=0; i < $3; i++ ) { + l = lbase + (int)log10(i+1)+10; + id = malloc(l); + snprintf(id,l,"%s%u",$1,i); + add_identifier(id,0,NON_ARRAY_IDENTIFIER); + } + free($1); + } + | IDENTIFIER LEFT_SQUARE_BRACKET RIGHT_SQUARE_BRACKET { add_identifier($1,0,ARRAY_IDENTIFIER_NO_DIM); } + | IDENTIFIER LEFT_SQUARE_BRACKET INT_OPERAND RIGHT_SQUARE_BRACKET { add_identifier($1,$3,ARRAY_IDENTIFIER); } + ; + +var_spec_list: var_spec + | var_spec_list var_spec; + +var_spec: space_spec + | type_spec + | align_spec + | EXTERN_DIRECTIVE { add_extern_spec(); } + ; + +align_spec: ALIGN_DIRECTIVE INT_OPERAND { add_alignment_spec($2); } + +space_spec: REG_DIRECTIVE { add_space_spec(REG_DIRECTIVE); } + | SREG_DIRECTIVE { add_space_spec(SREG_DIRECTIVE); } + | addressable_spec + ; + +addressable_spec: CONST_DIRECTIVE { add_space_spec(CONST_DIRECTIVE); } + | GLOBAL_DIRECTIVE { add_space_spec(GLOBAL_DIRECTIVE); } + | LOCAL_DIRECTIVE { add_space_spec(LOCAL_DIRECTIVE); } + | PARAM_DIRECTIVE { add_space_spec(PARAM_DIRECTIVE); } + | SHARED_DIRECTIVE { add_space_spec(SHARED_DIRECTIVE); } + | SURF_DIRECTIVE { add_space_spec(SURF_DIRECTIVE); } + | TEX_DIRECTIVE { add_space_spec(TEX_DIRECTIVE); } + ; + +type_spec: scalar_type + | vector_spec scalar_type + ; + +vector_spec: V2_TYPE { add_option(V2_TYPE); } + | V3_TYPE { add_option(V3_TYPE); } + | V4_TYPE { add_option(V4_TYPE); } + ; + +scalar_type: S8_TYPE { add_scalar_type_spec( S8_TYPE ); } + | S16_TYPE { add_scalar_type_spec( S16_TYPE ); } + | S32_TYPE { add_scalar_type_spec( S32_TYPE ); } + | S64_TYPE { add_scalar_type_spec( S64_TYPE ); } + | U8_TYPE { add_scalar_type_spec( U8_TYPE ); } + | U16_TYPE { add_scalar_type_spec( U16_TYPE ); } + | U32_TYPE { add_scalar_type_spec( U32_TYPE ); } + | U64_TYPE { add_scalar_type_spec( U64_TYPE ); } + | F16_TYPE { add_scalar_type_spec( F16_TYPE ); } + | F32_TYPE { add_scalar_type_spec( F32_TYPE ); } + | F64_TYPE { add_scalar_type_spec( F64_TYPE ); } + | B8_TYPE { add_scalar_type_spec( B8_TYPE ); } + | B16_TYPE { add_scalar_type_spec( B16_TYPE ); } + | B32_TYPE { add_scalar_type_spec( B32_TYPE ); } + | B64_TYPE { add_scalar_type_spec( B64_TYPE ); } + | PRED_TYPE { add_scalar_type_spec( PRED_TYPE ); } + ; + +initializer_list: LEFT_BRACE literal_list RIGHT_BRACE { add_array_initializer(); } + | LEFT_BRACE initializer_list RIGHT_BRACE { syntax_not_implemented(); } + +literal_list: literal_operand + | literal_operand COMMA literal_list; + +instruction_statement: instruction SEMI_COLON + | IDENTIFIER COLON { add_label($1); } + | pred_spec instruction SEMI_COLON; + +instruction: opcode_spec LEFT_PAREN operand RIGHT_PAREN { set_return(); } COMMA operand COMMA LEFT_PAREN operand_list RIGHT_PAREN + | opcode_spec operand COMMA LEFT_PAREN operand_list RIGHT_PAREN + | opcode_spec operand_list + | opcode_spec + ; + +opcode_spec: OPCODE { add_opcode($1); } option_list + | OPCODE { add_opcode($1); } + +pred_spec: PRED IDENTIFIER { add_pred($2,0); } + | PRED EXCLAMATION IDENTIFIER { add_pred($3,1); } + ; + +option_list: option + | option option_list ; + +option: type_spec + | compare_spec + | addressable_spec + | rounding_mode + | UNI_OPTION { add_option(UNI_OPTION); } + | WIDE_OPTION { add_option(WIDE_OPTION); } + | ANY_OPTION { add_option(ANY_OPTION); } + | ALL_OPTION { add_option(ALL_OPTION); } + | GLOBAL_OPTION { add_option(GLOBAL_OPTION); } + | CTA_OPTION { add_option(CTA_OPTION); } + | GEOM_MODIFIER_1D { add_option(GEOM_MODIFIER_1D); } + | GEOM_MODIFIER_2D { add_option(GEOM_MODIFIER_2D); } + | GEOM_MODIFIER_3D { add_option(GEOM_MODIFIER_3D); } + | SAT_OPTION { add_option(SAT_OPTION); } + | FTZ_OPTION { add_option(FTZ_OPTION); } + | APPROX_OPTION { add_option(APPROX_OPTION); } + | FULL_OPTION { add_option(FULL_OPTION); } + | atomic_operation_spec ; + +atomic_operation_spec: ATOMIC_AND { add_option(ATOMIC_AND); } + | ATOMIC_OR { add_option(ATOMIC_OR); } + | ATOMIC_XOR { add_option(ATOMIC_XOR); } + | ATOMIC_CAS { add_option(ATOMIC_CAS); } + | ATOMIC_EXCH { add_option(ATOMIC_EXCH); } + | ATOMIC_ADD { add_option(ATOMIC_ADD); } + | ATOMIC_INC { add_option(ATOMIC_INC); } + | ATOMIC_DEC { add_option(ATOMIC_DEC); } + | ATOMIC_MIN { add_option(ATOMIC_MIN); } + | ATOMIC_MAX { add_option(ATOMIC_MAX); } + ; + +rounding_mode: floating_point_rounding_mode + | integer_rounding_mode; + +floating_point_rounding_mode: RN_OPTION { add_option(RN_OPTION); } + | RZ_OPTION { add_option(RZ_OPTION); } + | RM_OPTION { add_option(RM_OPTION); } + | RP_OPTION { add_option(RP_OPTION); } + ; + +integer_rounding_mode: RNI_OPTION { add_option(RNI_OPTION); } + | RZI_OPTION { add_option(RZI_OPTION); } + | RMI_OPTION { add_option(RMI_OPTION); } + | RPI_OPTION { add_option(RPI_OPTION); } + ; + +compare_spec:EQ_OPTION { add_option(EQ_OPTION); } + | NE_OPTION { add_option(NE_OPTION); } + | LT_OPTION { add_option(LT_OPTION); } + | LE_OPTION { add_option(LE_OPTION); } + | GT_OPTION { add_option(GT_OPTION); } + | GE_OPTION { add_option(GE_OPTION); } + | LO_OPTION { add_option(LO_OPTION); } + | LS_OPTION { add_option(LS_OPTION); } + | HI_OPTION { add_option(HI_OPTION); } + | HS_OPTION { add_option(HS_OPTION); } + | EQU_OPTION { add_option(EQU_OPTION); } + | NEU_OPTION { add_option(NEU_OPTION); } + | LTU_OPTION { add_option(LTU_OPTION); } + | LEU_OPTION { add_option(LEU_OPTION); } + | GTU_OPTION { add_option(GTU_OPTION); } + | GEU_OPTION { add_option(GEU_OPTION); } + | NUM_OPTION { add_option(NUM_OPTION); } + | NAN_OPTION { add_option(NAN_OPTION); } + ; + +operand_list: operand + | operand COMMA operand_list; + +operand: IDENTIFIER { add_scalar_operand( $1 ); } + | EXCLAMATION IDENTIFIER { add_neg_pred_operand( $2 ); } + | memory_operand + | literal_operand + | builtin_operand + | vector_operand + | tex_operand + | IDENTIFIER PLUS INT_OPERAND { add_address_operand($1,$3); } + ; + +vector_operand: LEFT_BRACE IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE { add_2vector_operand($2,$4); } + | LEFT_BRACE IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE { add_3vector_operand($2,$4,$6); } + | LEFT_BRACE IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE { add_4vector_operand($2,$4,$6,$8); } + ; + +tex_operand: LEFT_SQUARE_BRACKET IDENTIFIER COMMA + LEFT_BRACE IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE + RIGHT_SQUARE_BRACKET { + add_scalar_operand($2); + add_4vector_operand($5,$7,$9,$11); + } + +builtin_operand: SPECIAL_REGISTER DIMENSION_MODIFIER { add_builtin_operand($1,$2); } + | SPECIAL_REGISTER { add_builtin_operand($1,-1); } + ; + +memory_operand : LEFT_SQUARE_BRACKET address_expression RIGHT_SQUARE_BRACKET { add_memory_operand(); } + +literal_operand : INT_OPERAND { add_literal_int($1); } + | FLOAT_OPERAND { add_literal_float($1); } + | DOUBLE_OPERAND { add_literal_double($1); } + ; + +address_expression: IDENTIFIER { add_address_operand($1,0); } + | IDENTIFIER PLUS INT_OPERAND { add_address_operand($1,$3); } + ; + +%% + +extern int ptx_lineno; +extern const char *g_filename; + +void syntax_not_implemented() +{ + printf("Parse error (%s:%u): this syntax is not (yet) implemented:\n",g_filename,ptx_lineno); + ptx_error(NULL); + abort(); +} diff --git a/src/cuda-sim/ptx_ir.cc b/src/cuda-sim/ptx_ir.cc new file mode 100644 index 0000000..542426a --- /dev/null +++ b/src/cuda-sim/ptx_ir.cc @@ -0,0 +1,1283 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "ptx_ir.h" +#include "ptx.tab.h" +#include "opcodes.h" +#include <stdio.h> +#include <stdlib.h> +#include <list> +#include <assert.h> +#include <algorithm> +#include <stdarg.h> + +extern unsigned g_max_regs_per_thread; +extern "C" int ptx_error( const char *s ); +void gpgpu_ptx_sim_bindTextureToArray(const struct textureReference* texref, struct cudaArray* array); //texture functions +struct cudaArray* gpgpu_ptx_sim_accessArrayofTexture(struct textureReference* texref); +void gpgpu_ptx_sim_bindNameToTexture(const char* name, struct textureReference* texref); +struct textureReference* gpgpu_ptx_sim_accessTextureofName(const char* name); +const char* gpgpu_ptx_sim_findNamefromTexture(const struct textureReference* texref); +int gpgpu_ptx_sim_sizeofTexture(const char* name); + +// the program intermediate representation... +symbol_table *g_global_symbol_table = NULL; +symbol_table *g_current_symbol_table = NULL; +std::list<ptx_instruction*> g_instructions; +symbol *g_last_symbol = NULL; +std::map<std::string,symbol_table*> g_sym_name_to_symbol_table; + +int g_error_detected = 0; + +// type specifier stuff: +int g_space_spec = -1; +int g_scalar_type_spec = -1; +int g_vector_spec = -1; +int g_alignment_spec = -1; +int g_extern_spec = 0; + +// variable declaration stuff: +type_info *g_var_type = NULL; + +// instruction definition stuff: +const symbol *g_pred; +int g_neg_pred; +symbol *g_label; +int g_opcode = -1; +std::list<operand_info> g_operands; +std::list<int> g_options; +std::list<int> g_scalar_type; + +extern bool g_debug_ir_generation; +extern const char *g_filename; +extern int ptx_lineno; +extern int g_debug_execution; +extern int g_debug_thread_uid; + + +#define DPRINTF(...) \ + if( g_debug_ir_generation ) { \ + printf(" %s:%u => ",g_filename,ptx_lineno); \ + printf(" (%s:%u) ", __FILE__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } + +unsigned g_entry_func_param_index=0; +function_info *g_func_info = NULL; +function_info *g_entrypoint_func_info = NULL; +symbol_table *g_entrypoint_symbol_table = NULL; +std::map<unsigned,std::string> g_ptx_token_decode; +operand_info g_return_var; + +void init_parser() +{ + g_global_symbol_table = g_current_symbol_table = new symbol_table("global",0,NULL); + ptx_lineno = 1; + +#define DEF(X,Y) g_ptx_token_decode[X] = Y; +#include "ptx_parser_decode.def" +#undef DEF +} + +void init_directive_state() +{ + DPRINTF("init_directive_state"); + g_space_spec=-1; + g_scalar_type_spec=-1; + g_vector_spec=-1; + g_opcode=-1; + g_alignment_spec = -1; + g_extern_spec = 0; + g_scalar_type.clear(); + g_operands.clear(); + g_last_symbol = NULL; +} + +void init_instruction_state() +{ + DPRINTF("init_instruction_state"); + g_pred = NULL; + g_neg_pred = 0; + g_label = NULL; + g_opcode = -1; + g_options.clear(); + g_return_var = operand_info(); + init_directive_state(); +} + +extern void register_ptx_function( const char *name, function_info *impl, symbol_table *symtab ); + +static int g_entry_point; + +void start_function( int entry_point ) +{ + DPRINTF("start_function"); + init_directive_state(); + init_instruction_state(); + g_entry_point = entry_point; + g_func_info = NULL; + g_entry_func_param_index=0; +} + +char *g_add_identifier_cached__identifier = NULL; +int g_add_identifier_cached__array_dim; +int g_add_identifier_cached__array_ident; + +void add_function_name( const char *name ) +{ + DPRINTF("add_function_name %s %s", name, ((g_entry_point)?"(entrypoint)":"")); + bool prior_decl = g_global_symbol_table->add_function_decl( name, g_entry_point, &g_func_info, &g_current_symbol_table ); + if( g_entry_point ) { + g_entrypoint_func_info = g_func_info; + g_entrypoint_symbol_table = g_current_symbol_table; + } + if( g_add_identifier_cached__identifier ) { + add_identifier( g_add_identifier_cached__identifier, + g_add_identifier_cached__array_dim, + g_add_identifier_cached__array_ident ); + free( g_add_identifier_cached__identifier ); + g_add_identifier_cached__identifier = NULL; + g_func_info->add_return_var( g_last_symbol ); + init_directive_state(); + } + if( prior_decl ) { + g_func_info->remove_args(); + } + g_global_symbol_table->add_function( g_func_info ); +} + +void add_directive() +{ + DPRINTF("add_directive"); + init_directive_state(); +} + +#define mymax(a,b) ((a)>(b)?(a):(b)) + +void gpgpu_ptx_assemble( std::string kname, void *kinfo ); + +void end_function() +{ + DPRINTF("end_function"); + + init_directive_state(); + init_instruction_state(); + g_max_regs_per_thread = mymax( g_max_regs_per_thread, (g_current_symbol_table->next_reg_num()-1)); + g_func_info->add_inst( g_instructions ); + g_instructions.clear(); + gpgpu_ptx_assemble( g_func_info->get_name(), g_func_info ); + + DPRINTF("function %s, PC = %d\n", g_func_info->get_name().c_str(), g_func_info->get_start_PC()); +} + +extern int ptx_lineno; +extern const char *g_filename; + +#define parse_error(msg, ...) parse_error_impl(__FILE__,__LINE__, msg, ##__VA_ARGS__) +#define parse_assert(cond,msg, ...) parse_assert_impl((cond),__FILE__,__LINE__, msg, ##__VA_ARGS__) + +void parse_error_impl( const char *file, unsigned line, const char *msg, ... ) +{ + va_list ap; + char buf[1024]; + va_start(ap,msg); + vsnprintf(buf,1024,msg,ap); + va_end(ap); + + g_error_detected = 1; + printf("%s:%u: Parse error: %s (%s:%u)\n\n", g_filename, ptx_lineno, buf, file, line); + ptx_error(NULL); + abort(); + exit(1); +} + +void parse_assert_impl( int test_value, const char *file, unsigned line, const char *msg, ... ) +{ + va_list ap; + char buf[1024]; + va_start(ap,msg); + vsnprintf(buf,1024,msg,ap); + va_end(ap); + + if ( test_value == 0 ) + parse_error_impl(file,line, msg); +} + +extern "C" char linebuf[1024]; + + +void set_return() +{ + parse_assert( (g_opcode == CALL_OP), "only call can have return value"); + g_operands.front().set_return(); + g_return_var = g_operands.front(); +} + +std::map<std::string,std::map<unsigned,const ptx_instruction*> > g_inst_lookup; + +const ptx_instruction *ptx_instruction_lookup( const char *filename, unsigned linenumber ) +{ + std::map<std::string,std::map<unsigned,const ptx_instruction*> >::iterator f=g_inst_lookup.find(filename); + if( f == g_inst_lookup.end() ) + return NULL; + std::map<unsigned,const ptx_instruction*>::iterator l=f->second.find(linenumber); + if( l == f->second.end() ) + return NULL; + return l->second; +} + +void add_instruction() +{ + DPRINTF("add_instruction: %s", ((g_opcode>0)?g_opcode_string[g_opcode]:"<label>") ); + ptx_instruction *i = new ptx_instruction( g_opcode, + g_pred, + g_neg_pred, + g_label, + g_operands, + g_return_var, + g_options, + g_scalar_type, + g_space_spec, + g_filename, + ptx_lineno, + linebuf ); + g_instructions.push_back(i); + g_inst_lookup[g_filename][ptx_lineno] = i; + init_instruction_state(); +} + +void add_variables() +{ + DPRINTF("add_variables"); + if ( !g_operands.empty() ) { + assert( g_last_symbol != NULL ); + g_last_symbol->add_initializer(g_operands); + } + init_directive_state(); +} + +void set_variable_type() +{ + DPRINTF("set_variable_type space_spec=%s scalar_type_spec=%s", + g_ptx_token_decode[g_space_spec].c_str(), + g_ptx_token_decode[g_scalar_type_spec].c_str() ); + parse_assert( g_space_spec != -1, "variable has no space specification" ); + parse_assert( g_scalar_type_spec != -1, "variable has no type information" ); // need to extend for structs? + g_var_type = g_current_symbol_table->add_type( g_space_spec, + g_scalar_type_spec, + g_vector_spec, + g_alignment_spec, + g_extern_spec ); +} + +bool check_for_duplicates( const char *identifier ) +{ + const symbol *s = g_current_symbol_table->lookup(identifier); + return ( s != NULL ); +} + +extern std::set<std::string> g_globals; +extern std::set<std::string> g_constants; + +int g_func_decl = 0; +int g_ident_add_uid = 0; +unsigned g_const_alloc = 1; + +void add_identifier( const char *identifier, int array_dim, unsigned array_ident ) +{ + if( g_func_decl && (g_func_info == NULL) ) { + // return variable decl... + assert( g_add_identifier_cached__identifier == NULL ); + g_add_identifier_cached__identifier = strdup(identifier); + g_add_identifier_cached__array_dim = array_dim; + g_add_identifier_cached__array_ident = array_ident; + return; + } + DPRINTF("add_identifier \"%s\" (%u)", identifier, g_ident_add_uid); + g_ident_add_uid++; + type_info *type = g_var_type; + type_info_key ti = type->get_key(); + int basic_type; + int regnum; + size_t num_bits; + unsigned addr, addr_pad; + type_decode(ti.scalar_type(),num_bits,basic_type); + + bool duplicates = check_for_duplicates( identifier ); + if( duplicates ) { + symbol *s = g_current_symbol_table->lookup(identifier); + g_last_symbol = s; + if( g_func_decl ) + return; + std::string msg = std::string(identifier) + " was delcared previous at " + s->decl_location(); + parse_error(msg.c_str()); + } + + assert( g_var_type != NULL ); + switch ( array_ident ) { + case ARRAY_IDENTIFIER: + type = g_current_symbol_table->get_array_type(type,array_dim); + num_bits = array_dim * num_bits; + break; + case ARRAY_IDENTIFIER_NO_DIM: + type = g_current_symbol_table->get_array_type(type,(unsigned)-1); + num_bits = 0; + break; + default: + break; + } + g_last_symbol = g_current_symbol_table->add_variable(identifier,type,g_filename,ptx_lineno); + switch ( g_space_spec ) { + case REG_DIRECTIVE: { + regnum = g_current_symbol_table->next_reg_num(); + int arch_regnum = -1; + for (int d = 0; d < strlen(identifier); d++) { + if (isdigit(identifier[d])) { + sscanf(identifier + d, "%d", &arch_regnum); + break; + } + } + if (strcmp(identifier, "%sp") == 0) { + arch_regnum = 0; + } + g_last_symbol->set_regno(regnum, arch_regnum); + } break; + case SHARED_DIRECTIVE: + printf("GPGPU-Sim PTX: allocating shared region for \"%s\" from 0x%x to 0x%lx (shared memory space)\n", + identifier, + g_current_symbol_table->get_shared_next(), + g_current_symbol_table->get_shared_next() + num_bits/8 ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + addr = g_current_symbol_table->get_shared_next(); + addr_pad = num_bits ? (((num_bits/8) - (addr % (num_bits/8))) % (num_bits/8)) : 0; + g_last_symbol->set_address( addr+addr_pad ); + g_current_symbol_table->alloc_shared( num_bits/8 + addr_pad ); + break; + case CONST_DIRECTIVE: + if( array_ident == ARRAY_IDENTIFIER_NO_DIM ) { + printf("GPGPU-Sim PTX: deferring allocation of constant region for \"%s\" (need size information)\n", identifier ); + } else { + printf("GPGPU-Sim PTX: allocating constant region for \"%s\" from 0x%x to 0x%lx (global memory space) %u\n", + identifier, + g_current_symbol_table->get_global_next(), + g_current_symbol_table->get_global_next() + num_bits/8, + g_const_alloc++ ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + addr = g_current_symbol_table->get_global_next(); + addr_pad = num_bits ? (((num_bits/8) - (addr % (num_bits/8))) % (num_bits/8)) : 0; + g_last_symbol->set_address( addr + addr_pad ); + g_current_symbol_table->alloc_global( num_bits/8 + addr_pad ); + } + if( g_current_symbol_table == g_global_symbol_table ) { + g_constants.insert( identifier ); + } + assert( g_current_symbol_table != NULL ); + g_sym_name_to_symbol_table[ identifier ] = g_current_symbol_table; + break; + case GLOBAL_DIRECTIVE: + printf("GPGPU-Sim PTX: allocating global region for \"%s\" from 0x%x to 0x%lx (global memory space)\n", + identifier, + g_current_symbol_table->get_global_next(), + g_current_symbol_table->get_global_next() + num_bits/8 ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + addr = g_current_symbol_table->get_global_next(); + addr_pad = num_bits ? (((num_bits/8) - (addr % (num_bits/8))) % (num_bits/8)) : 0; + g_last_symbol->set_address( addr+addr_pad ); + g_current_symbol_table->alloc_global( num_bits/8 + addr_pad ); + g_globals.insert( identifier ); + assert( g_current_symbol_table != NULL ); + g_sym_name_to_symbol_table[ identifier ] = g_current_symbol_table; + break; + case LOCAL_DIRECTIVE: + printf("GPGPU-Sim PTX: allocating local region for \"%s\" from 0x%x to 0x%lx (local memory space)\n", + identifier, + g_current_symbol_table->get_local_next(), + g_current_symbol_table->get_local_next() + num_bits/8 ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + g_last_symbol->set_address( g_current_symbol_table->get_local_next() ); + g_current_symbol_table->alloc_local( num_bits/8 ); + break; + case TEX_DIRECTIVE: + printf("GPGPU-Sim PTX: encountered texture directive %s.\n", identifier); + break; + default: + break; + } + + + if ( ti.is_param() ) { + g_func_info->add_param_name_and_type(g_entry_func_param_index,identifier, ti.scalar_type() ); + g_entry_func_param_index++; + } +} + +void add_function_arg() +{ + if( g_func_info ) { + DPRINTF("add_function_arg \"%s\"", g_last_symbol->name().c_str() ); + g_func_info->add_arg(g_last_symbol); + } +} + +void add_extern_spec() +{ + DPRINTF("add_extern_spec"); + g_extern_spec = 1; +} + +void add_alignment_spec( int spec ) +{ + DPRINTF("add_alignment_spec"); + parse_assert( g_alignment_spec == -1, "multiple .align specifiers per variable declaration not allowed." ); + g_alignment_spec = spec; +} + +void add_space_spec( int spec ) +{ + DPRINTF("add_space_spec \"%s\"", g_ptx_token_decode[spec].c_str() ); + parse_assert( g_space_spec == -1, "multiple space specifiers not allowed." ); + g_space_spec = spec; +} + +void add_vector_spec(int spec ) +{ + DPRINTF("add_vector_spec"); + parse_assert( g_vector_spec == -1, "multiple vector specifiers not allowed." ); + g_vector_spec = spec; +} + +void add_scalar_type_spec( int type_spec ) +{ + DPRINTF("add_scalar_type_spec \"%s\"", g_ptx_token_decode[type_spec].c_str()); + g_scalar_type.push_back( type_spec ); + if ( g_scalar_type.size() > 1 ) { + parse_assert( (g_opcode == -1) || (g_opcode == CVT_OP) || (g_opcode == SET_OP) || (g_opcode == SLCT_OP) + || (g_opcode == TEX_OP), + "only cvt, set, slct, and tex can have more than one type specifier."); + } + g_scalar_type_spec = type_spec; +} + +void add_label( const char *identifier ) +{ + DPRINTF("add_label"); + symbol *s = g_current_symbol_table->lookup(identifier); + if ( s != NULL ) { + g_label = s; + } else { + g_label = g_current_symbol_table->add_variable(identifier,NULL,g_filename,ptx_lineno); + } +} + +void add_opcode( int opcode ) +{ + g_opcode = opcode; +} + +void add_pred( const char *identifier, int neg ) +{ + DPRINTF("add_pred"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + std::string msg = std::string("predicate \"") + identifier + "\" has no declaration."; + parse_error( msg.c_str() ); + } + g_pred = s; + g_neg_pred = neg; +} + +void add_option( int option ) +{ + DPRINTF("add_option"); + g_options.push_back( option ); +} + +void add_2vector_operand( const char *d1, const char *d2 ) +{ + DPRINTF("add_2vector_operand"); + const symbol *s1 = g_current_symbol_table->lookup(d1); + const symbol *s2 = g_current_symbol_table->lookup(d2); + parse_assert( s1 != NULL && s2 != NULL, "v2 component(s) missing declarations."); + g_operands.push_back( operand_info(s1,s2,NULL,NULL) ); +} + +void add_3vector_operand( const char *d1, const char *d2, const char *d3 ) +{ + DPRINTF("add_3vector_operand"); + const symbol *s1 = g_current_symbol_table->lookup(d1); + const symbol *s2 = g_current_symbol_table->lookup(d2); + const symbol *s3 = g_current_symbol_table->lookup(d3); + parse_assert( s1 != NULL && s2 != NULL && s3 != NULL, "v3 component(s) missing declarations."); + g_operands.push_back( operand_info(s1,s2,s3,NULL) ); +} + +void add_4vector_operand( const char *d1, const char *d2, const char *d3, const char *d4 ) +{ + DPRINTF("add_4vector_operand"); + const symbol *s1 = g_current_symbol_table->lookup(d1); + const symbol *s2 = g_current_symbol_table->lookup(d2); + const symbol *s3 = g_current_symbol_table->lookup(d3); + const symbol *s4 = g_current_symbol_table->lookup(d4); + parse_assert( s1 != NULL && s2 != NULL && s3 != NULL && s4 != NULL, "v4 component(s) missing declarations."); + g_operands.push_back( operand_info(s1,s2,s3,s4) ); +} + +void add_builtin_operand( int builtin, int dim_modifier ) +{ + DPRINTF("add_builtin_operand"); + g_operands.push_back( operand_info(builtin,dim_modifier) ); +} + +void add_memory_operand() +{ + DPRINTF("add_memory_operand"); + assert( !g_operands.empty() ); + g_operands.back().make_memory_operand(); +} + +void add_literal_int( int value ) +{ + DPRINTF("add_literal_int"); + g_operands.push_back( operand_info(value) ); +} + +void add_literal_float( float value ) +{ + DPRINTF("add_literal_float"); + g_operands.push_back( operand_info(value) ); +} + +void add_literal_double( double value ) +{ + DPRINTF("add_literal_double"); + g_operands.push_back( operand_info(value) ); +} + +void add_scalar_operand( const char *identifier ) +{ + DPRINTF("add_scalar_operand"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + if ( g_opcode == BRA_OP ) { + // forward branch target... + s = g_current_symbol_table->add_variable(identifier,NULL,g_filename,ptx_lineno); + } else { + std::string msg = std::string("operand \"") + identifier + "\" has no declaration."; + parse_error( msg.c_str() ); + } + } + g_operands.push_back( operand_info(s) ); +} + +void add_neg_pred_operand( const char *identifier ) +{ + DPRINTF("add_neg_pred_operand"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + s = g_current_symbol_table->add_variable(identifier,NULL,g_filename,ptx_lineno); + } + operand_info op(s); + op.set_neg_pred(); + g_operands.push_back( op ); +} + +void add_address_operand( const char *identifier, int offset ) +{ + DPRINTF("add_address_operand"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + std::string msg = std::string("operand \"") + identifier + "\" has no declaration."; + parse_error( msg.c_str() ); + } + g_operands.push_back( operand_info(s,offset) ); +} + +unsigned symbol::sm_next_uid = 1; + +unsigned symbol::get_uid() +{ + unsigned result = sm_next_uid++; + return result; +} + +void symbol::add_initializer( const std::list<operand_info> &init ) +{ + m_initializer = init; +} + +void symbol::print_info(FILE *fp) const +{ + fprintf(fp,"uid:%u, decl:%s, type:%p, ", m_uid, m_decl_location.c_str(), m_type ); + if( m_address_valid ) + fprintf(fp,"<address valid>, "); + if( m_is_label ) + fprintf(fp," is_label "); + if( m_is_shared ) + fprintf(fp," is_shared "); + if( m_is_const ) + fprintf(fp," is_const "); + if( m_is_global ) + fprintf(fp," is_global "); + if( m_is_local ) + fprintf(fp," is_local "); + if( m_is_tex ) + fprintf(fp," is_tex "); + if( m_is_func_addr ) + fprintf(fp," is_func_addr "); + if( m_function ) + fprintf(fp," %p ", m_function ); +} + +symbol_table::symbol_table() +{ + assert(0); +} + +symbol_table::symbol_table( const char *scope_name, unsigned entry_point, symbol_table *parent ) +{ + m_scope_name = std::string(scope_name); + m_reg_allocator=0; + m_shared_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_const_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_global_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_local_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_parent = parent; + if ( m_parent ) { + m_shared_next = m_parent->m_shared_next; + m_global_next = m_parent->m_global_next; + } +} + +void symbol_table::set_name( const char *name ) +{ + m_scope_name = std::string(name); +} + +symbol *symbol_table::lookup( const char *identifier ) +{ + std::string key(identifier); + std::map<std::string, symbol *>::iterator i = m_symbols.find(key); + if ( i != m_symbols.end() ) { + return i->second; + } + if ( m_parent ) { + return m_parent->lookup(identifier); + } + return NULL; +} + +symbol *symbol_table::add_variable( const char *identifier, const type_info *type, const char *filename, unsigned line ) +{ + char buf[1024]; + std::string key(identifier); + assert( m_symbols.find(key) == m_symbols.end() ); + snprintf(buf,1024,"%s:%u",filename,line); + symbol *s = new symbol(identifier,type,buf); + m_symbols[ key ] = s; + + if ( type != NULL && type->get_key().is_param() ) { + m_params.push_back(s); + } + if ( type != NULL && type->get_key().is_global() ) { + m_globals.push_back(s); + } + if ( type != NULL && type->get_key().is_const() ) { + m_consts.push_back(s); + } + + return s; +} + +void symbol_table::add_function( function_info *func ) +{ + std::map<std::string, symbol *>::iterator i = m_symbols.find( func->get_name() ); + if( i != m_symbols.end() ) + return; + char buf[1024]; + snprintf(buf,1024,"%s:%u",g_filename,ptx_lineno); + type_info *type = add_type( func ); + symbol *s = new symbol(func->get_name().c_str(),type,buf); + s->set_function(func); + m_symbols[ func->get_name() ] = s; +} + +bool symbol_table::add_function_decl( const char *name, int entry_point, function_info **func_info, symbol_table **sym_table ) +{ + std::string key = std::string(name); + bool prior_decl = false; + if( m_function_info_lookup.find(key) != m_function_info_lookup.end() ) { + *func_info = m_function_info_lookup[key]; + prior_decl = true; + } else { + *func_info = new function_info(entry_point); + (*func_info)->set_name(name); + m_function_info_lookup[key] = *func_info; + } + + if( m_function_symtab_lookup.find(key) != m_function_symtab_lookup.end() ) { + assert( prior_decl ); + *sym_table = m_function_symtab_lookup[key]; + } else { + assert( !prior_decl ); + *sym_table = new symbol_table( "", entry_point, g_global_symbol_table ); + symbol *null_reg = (*sym_table)->add_variable("_",NULL,"",0); + null_reg->set_regno(0, 0); + (*sym_table)->set_name(name); + (*func_info)->set_symtab(*sym_table); + m_function_symtab_lookup[key] = *sym_table; + register_ptx_function(name,*func_info,*sym_table); + } + return prior_decl; +} + +type_info *symbol_table::add_type( int space_spec, int scalar_type_spec, int vector_spec, int alignment_spec, int extern_spec ) +{ + type_info_key t(space_spec,scalar_type_spec,vector_spec,alignment_spec,extern_spec,0); + type_info *pt; + pt = new type_info(this,t); + return pt; +} + +type_info *symbol_table::add_type( function_info *func ) +{ + type_info_key t; + type_info *pt; + t.set_is_func(); + pt = new type_info(this,t); + return pt; +} + +type_info *symbol_table::get_array_type( type_info *base_type, unsigned array_dim ) +{ + type_info_key t = base_type->get_key(); + t.set_array_dim(array_dim); + type_info *pt; + pt = m_types[t] = new type_info(this,t); + return pt; +} + +void symbol_table::set_label_address( const symbol *label, unsigned addr ) +{ + std::map<std::string, symbol *>::iterator i=m_symbols.find(label->name()); + assert( i != m_symbols.end() ); + symbol *s = i->second; + s->set_label_address(addr); +} + +void symbol_table::dump() +{ + printf("\n\n"); + printf("Symbol table for \"%s\":\n", m_scope_name.c_str() ); + std::map<std::string, symbol *>::iterator i; + for( i=m_symbols.begin(); i!=m_symbols.end(); i++ ) { + printf("%30s : ", i->first.c_str() ); + if( i->second ) + i->second->print_info(stdout); + else + printf(" <no symbol object> "); + printf("\n"); + } + printf("\n"); +} + +unsigned operand_info::sm_next_uid=1; + +unsigned operand_info::get_uid() +{ + unsigned result = sm_next_uid++; + return result; +} + +void add_array_initializer() +{ + g_last_symbol->add_initializer(g_operands); +} + + +std::list<ptx_instruction*>::iterator function_info::find_next_real_instruction( std::list<ptx_instruction*>::iterator i) +{ + while( (i != m_instructions.end()) && (*i)->is_label() ) + i++; + return i; +} + +void function_info::create_basic_blocks() +{ + std::list<ptx_instruction*> leaders; + std::list<ptx_instruction*>::iterator i, l; + + // first instruction is a leader + i=m_instructions.begin(); + leaders.push_back(*i); + i++; + while( i!=m_instructions.end() ) { + ptx_instruction *pI = *i; + if( pI->is_label() ) { + leaders.push_back(pI); + i = find_next_real_instruction(++i); + } else { + switch( pI->get_opcode() ) { + case BRA_OP: case RET_OP: case EXIT_OP: + i++; + if( i != m_instructions.end() ) + leaders.push_back(*i); + i = find_next_real_instruction(i); + break; + case CALL_OP: + if( pI->has_pred() ) { + printf("GPGPU-Sim PTX: Warning found predicated call\n"); + i++; + if( i != m_instructions.end() ) + leaders.push_back(*i); + i = find_next_real_instruction(i); + } else i++; + break; + default: + i++; + } + } + } + + if( leaders.empty() ) { + printf("GPGPU-Sim PTX: Function \'%s\' has no basic blocks\n", m_name.c_str()); + return; + } + + unsigned bb_id = 0; + l=leaders.begin(); + i=m_instructions.begin(); + m_basic_blocks.push_back( new basic_block_t(bb_id++,*find_next_real_instruction(i),NULL,1,0) ); + ptx_instruction *last_real_inst=*(l++); + + for( ; i!=m_instructions.end(); i++ ) { + ptx_instruction *pI = *i; + if( l != leaders.end() && *i == *l ) { + // found start of next basic block + m_basic_blocks.back()->ptx_end = last_real_inst; + if( find_next_real_instruction(i) != m_instructions.end() ) { // if not bogus trailing label + m_basic_blocks.push_back( new basic_block_t(bb_id++,*find_next_real_instruction(i),NULL,0,0) ); + last_real_inst = *find_next_real_instruction(i); + } + // start search for next leader + l++; + } + pI->assign_bb( m_basic_blocks.back() ); + if( !pI->is_label() ) last_real_inst = pI; + } + m_basic_blocks.back()->ptx_end = last_real_inst; + m_basic_blocks.push_back( /*exit basic block*/ new basic_block_t(bb_id,NULL,NULL,0,1) ); +} + +void function_info::print_basic_blocks() +{ + printf("Printing basic blocks for function \'%s\':\n", m_name.c_str() ); + std::list<ptx_instruction*>::iterator ptx_itr; + unsigned last_bb=0; + for (ptx_itr = m_instructions.begin();ptx_itr != m_instructions.end(); ptx_itr++) { + if( (*ptx_itr)->get_bb() ) { + if( (*ptx_itr)->get_bb()->bb_id != last_bb ) { + printf("\n"); + last_bb = (*ptx_itr)->get_bb()->bb_id; + } + printf("bb_%02u\t: ", (*ptx_itr)->get_bb()->bb_id); + (*ptx_itr)->print_insn(); + printf("\n"); + } + } + printf("\nSummary of basic blocks for \'%s\':\n", m_name.c_str() ); + std::vector<basic_block_t*>::iterator bb_itr; + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + printf("bb_%02u\t:", (*bb_itr)->bb_id); + if ((*bb_itr)->ptx_begin) + printf(" first: %s\t", ((*bb_itr)->ptx_begin)->get_opcode_cstr()); + else printf(" first: NULL\t"); + if ((*bb_itr)->ptx_end) { + printf(" last: %s\t", ((*bb_itr)->ptx_end)->get_opcode_cstr()); + } else printf(" last: NULL\t"); + printf("\n"); + } + printf("\n"); +} + +void function_info::print_basic_block_links() +{ + printf("Printing basic blocks links for function \'%s\':\n", m_name.c_str() ); + std::vector<basic_block_t*>::iterator bb_itr; + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + printf("ID: %d\t:", (*bb_itr)->bb_id); + if ( !(*bb_itr)->predecessor_ids.empty() ) { + printf("Predecessors:"); + std::set<int>::iterator p; + for (p= (*bb_itr)->predecessor_ids.begin();p != (*bb_itr)->predecessor_ids.end();p++) { + printf(" %d", *p); + } + printf("\t"); + } + if ( !(*bb_itr)->successor_ids.empty() ) { + printf("Successors:"); + std::set<int>::iterator s; + for (s= (*bb_itr)->successor_ids.begin();s != (*bb_itr)->successor_ids.end();s++) { + printf(" %d", *s); + } + } + printf("\n"); + } +} +void function_info::connect_basic_blocks( ) //iterate across m_basic_blocks of function, connecting basic blocks together +{ + std::vector<basic_block_t*>::iterator bb_itr; + std::vector<basic_block_t*>::iterator bb_target_itr; + basic_block_t* exit_bb = m_basic_blocks.back(); + + //start from first basic block, which we know is the entry point + bb_itr = m_basic_blocks.begin(); + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + ptx_instruction *pI = (*bb_itr)->ptx_end; + if ((*bb_itr)->is_exit) //reached last basic block, no successors to link + continue; + if (pI->get_opcode() == RET_OP || pI->get_opcode() == EXIT_OP ) { + (*bb_itr)->successor_ids.insert(exit_bb->bb_id); + exit_bb->predecessor_ids.insert((*bb_itr)->bb_id); + if( pI->has_pred() ) { + printf("GPGPU-Sim PTX: Warning detected predicated return/exit.\n"); + // if predicated, add link to next block + unsigned next_addr = pI->get_m_instr_mem_index() + 1; + if( next_addr < m_instr_mem_size && m_instr_mem[next_addr] ) { + basic_block_t *next_bb = m_instr_mem[next_addr]->get_bb(); + (*bb_itr)->successor_ids.insert(next_bb->bb_id); + next_bb->predecessor_ids.insert((*bb_itr)->bb_id); + } + } + continue; + } else if (pI->get_opcode() == BRA_OP) { + //find successor and link that basic_block to this one + operand_info &target = pI->dst(); //get operand, e.g. target name + unsigned addr = labels[ target.name() ]; + ptx_instruction *target_pI = m_instr_mem[addr]; + basic_block_t *target_bb = target_pI->get_bb(); + (*bb_itr)->successor_ids.insert(target_bb->bb_id); + target_bb->predecessor_ids.insert((*bb_itr)->bb_id); + } + if ( !(pI->get_opcode()==BRA_OP && (!pI->has_pred())) ) { + // if basic block does not end in an unpredicated branch, + // then next basic block is also successor + // (this is better than testing for .uni) + unsigned next_addr = pI->get_m_instr_mem_index() + 1; + basic_block_t *next_bb = m_instr_mem[next_addr]->get_bb(); + (*bb_itr)->successor_ids.insert(next_bb->bb_id); + next_bb->predecessor_ids.insert((*bb_itr)->bb_id); + } else + assert(pI->get_opcode() == BRA_OP); + } +} + +void intersect( std::set<int> &A, const std::set<int> &B ) +{ + // return intersection of A and B in A + for( std::set<int>::iterator a=A.begin(); a!=A.end(); ) { + std::set<int>::iterator a_next = a; + a_next++; + if( B.find(*a) == B.end() ) { + A.erase(*a); + a = a_next; + } else + a++; + } +} + +bool is_equal( const std::set<int> &A, const std::set<int> &B ) +{ + if( A.size() != B.size() ) + return false; + for( std::set<int>::iterator b=B.begin(); b!=B.end(); b++ ) + if( A.find(*b) == A.end() ) + return false; + return true; +} + +void print_set(const std::set<int> &A) +{ + std::set<int>::iterator a; + for (a= A.begin(); a != A.end(); a++) { + printf("%d ", (*a)); + } + printf("\n"); +} + +void function_info::find_postdominators( ) +{ + // find postdominators using algorithm of Muchnick's Adv. Compiler Design & Implemmntation Fig 7.14 + printf("GPGPU-Sim PTX: Finding postdominators for \'%s\'...\n", m_name.c_str() ); + fflush(stdout); + assert( m_basic_blocks.size() >= 2 ); // must have a distinquished exit block + std::vector<basic_block_t*>::reverse_iterator bb_itr = m_basic_blocks.rbegin(); + (*bb_itr)->postdominator_ids.insert((*bb_itr)->bb_id); // the only postdominator of the exit block is the exit + for (++bb_itr;bb_itr != m_basic_blocks.rend();bb_itr++) { //copy all basic blocks to all postdominator lists EXCEPT for the exit block + for (unsigned i=0; i<m_basic_blocks.size(); i++) + (*bb_itr)->postdominator_ids.insert(i); + } + bool change = true; + while (change) { + change = false; + for ( int h = m_basic_blocks.size()-2/*skip exit*/; h >= 0 ; --h ) { + assert( m_basic_blocks[h]->bb_id == (unsigned)h ); + std::set<int> T; + for (unsigned i=0;i< m_basic_blocks.size();i++) + T.insert(i); + for ( std::set<int>::iterator s = m_basic_blocks[h]->successor_ids.begin();s != m_basic_blocks[h]->successor_ids.end();s++) + intersect(T, m_basic_blocks[*s]->postdominator_ids); + T.insert(h); + if (!is_equal(T,m_basic_blocks[h]->postdominator_ids)) { + change = true; + m_basic_blocks[h]->postdominator_ids = T; + } + } + } +} + +void function_info::find_ipostdominators( ) +{ + // find immediate postdominator blocks, using algorithm of + // Muchnick's Adv. Compiler Design & Implemmntation Fig 7.15 + printf("GPGPU-Sim PTX: Finding immediate postdominators for \'%s\'...\n", m_name.c_str() ); + fflush(stdout); + assert( m_basic_blocks.size() >= 2 ); // must have a distinquished exit block + for (unsigned i=0; i<m_basic_blocks.size(); i++) { //initialize Tmp(n) to all pdoms of n except for n + m_basic_blocks[i]->Tmp_ids = m_basic_blocks[i]->postdominator_ids; + assert( m_basic_blocks[i]->bb_id == i ); + m_basic_blocks[i]->Tmp_ids.erase(i); + } + for ( int n = m_basic_blocks.size()-2; n >=0;--n) { + // point iterator to basic block before the exit + for( std::set<int>::iterator s=m_basic_blocks[n]->Tmp_ids.begin(); s != m_basic_blocks[n]->Tmp_ids.end(); s++ ) { + int bb_s = *s; + for( std::set<int>::iterator t=m_basic_blocks[n]->Tmp_ids.begin(); t != m_basic_blocks[n]->Tmp_ids.end(); ) { + std::set<int>::iterator t_next = t; t_next++; // might erase thing pointed to be t, invalidating iterator t + if( *s == *t ) { + t = t_next; + continue; + } + int bb_t = *t; + if( m_basic_blocks[bb_s]->postdominator_ids.find(bb_t) != m_basic_blocks[bb_s]->postdominator_ids.end() ) + m_basic_blocks[n]->Tmp_ids.erase(bb_t); + t = t_next; + } + } + } + unsigned num_ipdoms=0; + for ( int n = m_basic_blocks.size()-1; n >=0;--n) { + assert( m_basic_blocks[n]->Tmp_ids.size() <= 1 ); + // if the above assert fails we have an error in either postdominator + // computation, the flow graph does not have a unique exit, or some other error + if( !m_basic_blocks[n]->Tmp_ids.empty() ) { + m_basic_blocks[n]->immediatepostdominator_id = *m_basic_blocks[n]->Tmp_ids.begin(); + num_ipdoms++; + } + } + assert( num_ipdoms == m_basic_blocks.size()-1 ); + // the exit node does not have an immediate post dominator, but everyone else should +} + +void function_info::print_postdominators() +{ + printf("Printing postdominators for function \'%s\':\n", m_name.c_str() ); + std::vector<int>::iterator bb_itr; + for (unsigned i = 0; i < m_basic_blocks.size(); i++) { + printf("ID: %d\t:", i); + for( std::set<int>::iterator j=m_basic_blocks[i]->postdominator_ids.begin(); j!=m_basic_blocks[i]->postdominator_ids.end(); j++) + printf(" %d", *j ); + printf("\n"); + } +} + +void function_info::print_ipostdominators() +{ + printf("Printing immediate postdominators for function \'%s\':\n", m_name.c_str() ); + std::vector<int>::iterator bb_itr; + for (unsigned i = 0; i < m_basic_blocks.size(); i++) { + printf("ID: %d\t:", i); + printf("%d\n", m_basic_blocks[i]->immediatepostdominator_id); + } +} + +unsigned function_info::get_num_reconvergence_pairs() +{ + if (!num_reconvergence_pairs) { + for (unsigned i=0; i< (m_basic_blocks.size()-1); i++) { //last basic block containing exit obviously won't have a pair + if (m_basic_blocks[i]->ptx_end->get_opcode() == BRA_OP) { + num_reconvergence_pairs++; + } + } + } + return num_reconvergence_pairs; +} + +void function_info::get_reconvergence_pairs(gpgpu_recon_t* recon_points) +{ + unsigned idx=0; //array index + for (unsigned i=0; i< (m_basic_blocks.size()-1); i++) { //last basic block containing exit obviously won't have a pair +#ifdef DEBUG_GET_RECONVERG_PAIRS + printf("i=%d\n", i); fflush(stdout); +#endif + if (m_basic_blocks[i]->ptx_end->get_opcode() == BRA_OP) { +#ifdef DEBUG_GET_RECONVERG_PAIRS + printf("\tbranch!\n"); + printf("\tbb_id=%d; ipdom=%d\n", m_basic_blocks[i]->bb_id, m_basic_blocks[i]->immediatepostdominator_id); + printf("\tm_instr_mem index=%d\n", m_basic_blocks[i]->ptx_end->get_m_instr_mem_index()); + fflush(stdout); +#endif + recon_points[idx].source_pc = m_basic_blocks[i]->ptx_end->get_PC(); +#ifdef DEBUG_GET_RECONVERG_PAIRS + printf("\trecon_points[idx].source_pc=%d\n", recon_points[idx].source_pc); +#endif + if( m_basic_blocks[m_basic_blocks[i]->immediatepostdominator_id]->ptx_begin ) { + recon_points[idx].target_pc = m_basic_blocks[m_basic_blocks[i]->immediatepostdominator_id]->ptx_begin->get_PC(); + } else { + // reconverge after function return + recon_points[idx].target_pc = -2; + } +#ifdef DEBUG_GET_RECONVERG_PAIRS + m_basic_blocks[m_basic_blocks[i]->immediatepostdominator_id]->ptx_begin->print_insn(); + printf("\trecon_points[idx].target_pc=%d\n", recon_points[idx].target_pc); fflush(stdout); +#endif + idx++; + } + } +} + +// interface with graphviz (print the graph in DOT language) for plotting +void function_info::print_basic_block_dot() +{ + printf("Basic Block in DOT\n"); + printf("digraph %s {\n", m_name.c_str()); + std::vector<basic_block_t*>::iterator bb_itr; + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + printf("\t"); + std::set<int>::iterator s; + for (s = (*bb_itr)->successor_ids.begin();s != (*bb_itr)->successor_ids.end();s++) { + unsigned succ_bb = *s; + printf("%d -> %d; ", (*bb_itr)->bb_id, succ_bb ); + } + printf("\n"); + } + printf("}\n"); +} + +extern "C" void add_file( unsigned num, const char *filename ) +{ + if( g_filename == NULL ) { + char *b = strdup(filename); + char *l=b; + char *n=b; + while( *n != '\0' ) { + if( *n == '/' ) + l = n+1; + n++; + } + + char *p = strtok(l,"."); + char buf[1024]; + snprintf(buf,1024,"%s.ptx",p); + + char *q = strtok(NULL,"."); + if( q && !strcmp(q,"cu") ) { + g_filename = strdup(buf); + } + + free( b ); + } + + g_current_symbol_table = g_global_symbol_table; +} + +extern "C" void *reset_symtab() +{ + void *result = g_current_symbol_table; + g_current_symbol_table = g_global_symbol_table; + return result; +} + +extern "C" void set_symtab(void*symtab) +{ + g_current_symbol_table = (symbol_table*)symtab; +} + +unsigned ptx_kernel_shmem_size( void *kernel_impl ) +{ + function_info *f = (function_info*)kernel_impl; + const struct gpgpu_ptx_sim_kernel_info *kernel_info = f->get_kernel_info(); + return kernel_info->smem; +} + +unsigned ptx_kernel_nregs( void *kernel_impl ) +{ + function_info *f = (function_info*)kernel_impl; + const struct gpgpu_ptx_sim_kernel_info *kernel_info = f->get_kernel_info(); + return kernel_info->regs; +} diff --git a/src/cuda-sim/ptx_ir.h b/src/cuda-sim/ptx_ir.h new file mode 100644 index 0000000..82d5b4c --- /dev/null +++ b/src/cuda-sim/ptx_ir.h @@ -0,0 +1,1223 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Dan O'Connor, Joey Ting, Henry Wong and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef ptx_ir_INCLUDED +#define ptx_ir_INCLUDED + +#ifdef __cplusplus + + #include <cstdlib> + #include <cstring> + #include <string> + #include <list> + #include <map> + #include <vector> + #include <assert.h> + + #include "ptx.tab.h" + #include "ptx_sim.h" + #include "dram_callback.h" + #include "../util.h" + + #include "memory.h" + +enum space_type { + undefined, inst_space +}; + + +class addr { /* need this because there are many distinct address spaces (global, local, param, tex, surf, shared) */ +public: + + addr() { m_space=undefined; m_addr = 0;} + void set_space( enum space_type space ); + operator unsigned() { return m_addr;} + +private: + enum space_type m_space; + unsigned m_addr; +}; + + +class type_info_key { +public: + type_info_key() + { + m_init = false; + } + type_info_key( int space_spec, int scalar_type_spec, int vector_spec, int alignment_spec, int extern_spec, int array_dim ) + { + m_init = true; + m_space_spec = space_spec; + m_scalar_type_spec = scalar_type_spec; + m_vector_spec = vector_spec; + m_alignment_spec = alignment_spec; + m_extern_spec = extern_spec; + m_array_dim = array_dim; + m_is_function = 0; + } + void set_is_func() + { + assert(!m_init); + m_init = true; + m_space_spec = 0; + m_scalar_type_spec = 0; + m_vector_spec = 0; + m_alignment_spec = 0; + m_extern_spec = 0; + m_array_dim = 0; + m_is_function = 1; + } + + void set_array_dim( int array_dim ) + { + m_array_dim = array_dim; + } + + bool is_reg() const { return m_space_spec == REG_DIRECTIVE;} + bool is_param() const { return m_space_spec == PARAM_DIRECTIVE;} + bool is_global() const { return m_space_spec == GLOBAL_DIRECTIVE;} + bool is_local() const { return m_space_spec == LOCAL_DIRECTIVE;} + bool is_shared() const { return m_space_spec == SHARED_DIRECTIVE;} + bool is_const() const { return m_space_spec == CONST_DIRECTIVE;} + bool is_tex() const { return m_space_spec == TEX_DIRECTIVE;} + bool is_func_addr() const { return m_is_function?true:false; } + int scalar_type() const { return m_scalar_type_spec;} +private: + bool m_init; + int m_space_spec; + int m_scalar_type_spec; + int m_vector_spec; + int m_alignment_spec; + int m_extern_spec; + int m_array_dim; + int m_is_function; + + friend class type_info_key_compare; +}; + +class symbol_table; + +struct type_info_key_compare { + bool operator()( const type_info_key &a, const type_info_key &b ) const + { + assert( a.m_init && b.m_init ); + if ( a.m_space_spec < b.m_space_spec ) return true; + if ( a.m_scalar_type_spec < b.m_scalar_type_spec ) return true; + if ( a.m_vector_spec < b.m_vector_spec ) return true; + if ( a.m_alignment_spec < b.m_alignment_spec ) return true; + if ( a.m_extern_spec < b.m_extern_spec ) return true; + if ( a.m_array_dim < b.m_array_dim ) return true; + if ( a.m_is_function < b.m_is_function ) return true; + + return false; + } +}; + +class type_info { +public: + type_info( symbol_table *scope, type_info_key t ) + { + m_type_info = t; + } + const type_info_key &get_key() const { return m_type_info;} + +private: + symbol_table *m_scope; + type_info_key m_type_info; +}; + +enum operand_type { + reg_t, vector_t, builtin_t, address_t, memory_t, float_op_t, double_op_t, int_t, + unsigned_t, symbolic_t, label_t, v_reg_t, v_float_op_t, v_double_op_t, + v_int_t, v_unsigned_t +}; + +class operand_info; + +class symbol { +public: + symbol( const char *name, const type_info *type, const char *location ) + { + m_uid = get_uid(); + m_name = name; + m_decl_location = location; + m_type = type; + m_address_valid = false; + m_is_label = false; + m_is_shared = false; + m_is_const = false; + m_is_global = false; + m_is_local = false; + m_is_tex = false; + m_is_func_addr = false; + m_reg_num_valid = false; + m_function = NULL; + if ( type ) m_is_shared = type->get_key().is_shared(); + if ( type ) m_is_const = type->get_key().is_const(); + if ( type ) m_is_global = type->get_key().is_global(); + if ( type ) m_is_local = type->get_key().is_local(); + if ( type ) m_is_tex = type->get_key().is_tex(); + if ( type ) m_is_func_addr = type->get_key().is_func_addr(); + } + const std::string &name() const { return m_name;} + const std::string &decl_location() const { return m_decl_location;} + const type_info *type() const { return m_type;} + addr_t get_address() const + { + assert( m_is_label || !m_type->get_key().is_reg() ); // todo : other assertions + assert( m_address_valid ); + return m_address; + } + function_info *get_pc() const + { + return m_function; + } + void set_regno( unsigned regno, unsigned arch_regno ) + { + m_reg_num_valid = true; + m_reg_num = regno; + m_arch_reg_num = arch_regno; + } + + void set_address( addr_t addr ) + { + m_address_valid = true; + m_address = addr; + } + void set_label_address( addr_t addr) + { + m_address_valid = true; + m_address = addr; + m_is_label = true; + } + void set_function( function_info *func ) + { + m_function = func; + m_is_func_addr = true; + } + + bool is_label() const { return m_is_label;} + bool is_shared() const { return m_is_shared;} + bool is_const() const { return m_is_const;} + bool is_global() const { return m_is_global;} + bool is_local() const { return m_is_local;} + bool is_tex() const { return m_is_tex;} + bool is_func_addr() const { return m_is_func_addr; } + + void add_initializer( const std::list<operand_info> &init ); + bool has_initializer() const + { + return m_initializer.size() > 0; + } + std::list<operand_info> get_initializer() const + { + return m_initializer; + } + unsigned reg_num() const + { + assert( m_reg_num_valid ); + return m_reg_num; + } + unsigned arch_reg_num() const + { + assert( m_reg_num_valid ); + return m_arch_reg_num; + } + void print_info(FILE *fp) const; + +private: + unsigned get_uid(); + unsigned m_uid; + const type_info *m_type; + std::string m_name; + std::string m_decl_location; + + unsigned m_address; + function_info *m_function; // used for function symbols + + bool m_address_valid; + bool m_is_label; + bool m_is_shared; + bool m_is_const; + bool m_is_global; + bool m_is_local; + bool m_is_tex; + bool m_is_func_addr; + unsigned m_reg_num; + unsigned m_arch_reg_num; + bool m_reg_num_valid; + + std::list<operand_info> m_initializer; + static unsigned sm_next_uid; +}; + + +class symbol_table { +public: + symbol_table(); + symbol_table( const char *scope_name, unsigned entry_point, symbol_table *parent ); + void set_name( const char *name ); + symbol* lookup( const char *identifier ); + std::string get_scope_name() const { return m_scope_name; } + symbol *add_variable( const char *identifier, const type_info *type, const char *filename, unsigned line ); + void add_function( function_info *func ); + bool add_function_decl( const char *name, int entry_point, function_info **func_info, symbol_table **symbol_table ); + type_info *add_type( int space_spec, int scalar_type_spec, int vector_spec, int alignment_spec, int extern_spec ); + type_info *add_type( function_info *func ); + type_info *get_array_type( type_info *base_type, unsigned array_dim ); + void set_label_address( const symbol *label, unsigned addr ); + unsigned next_reg_num() { return ++m_reg_allocator;} + addr_t get_shared_next() { return m_shared_next;} + addr_t get_global_next() { return m_global_next;} + addr_t get_local_next() { return m_local_next;} + addr_t get_tex_next() { return m_tex_next;} + void alloc_shared( unsigned num_bytes ) { m_shared_next += num_bytes;} + void alloc_global( unsigned num_bytes ) { m_global_next += num_bytes;} + void alloc_local( unsigned num_bytes ) { m_local_next += num_bytes;} + void alloc_tex( unsigned num_bytes ) { m_tex_next += num_bytes;} + + typedef std::list<symbol*>::iterator iterator; + + iterator param_iterator_begin() { return m_params.begin();} + iterator param_iterator_end() { return m_params.end();} + + iterator global_iterator_begin() { return m_globals.begin();} + iterator global_iterator_end() { return m_globals.end();} + + iterator const_iterator_begin() { return m_consts.begin();} + iterator const_iterator_end() { return m_consts.end();} + + void dump(); +private: + unsigned m_reg_allocator; + unsigned m_shared_next; + unsigned m_const_next; + unsigned m_global_next; + unsigned m_local_next; + unsigned m_tex_next; + + symbol_table *m_parent; + std::string m_scope_name; + std::map<std::string, symbol *> m_symbols; //map from name of register to pointers to the registers + std::map<type_info_key,type_info*,type_info_key_compare> m_types; + std::list<symbol*> m_params; + std::list<symbol*> m_globals; + std::list<symbol*> m_consts; + std::map<std::string,function_info*> m_function_info_lookup; + std::map<std::string,symbol_table*> m_function_symtab_lookup; +}; + +class operand_info { +public: + operand_info() + { + m_uid = get_uid(); + m_valid = false; + } + operand_info( const symbol *addr ) + { + m_uid = get_uid(); + m_valid = true; + if ( addr->is_label() ) { + m_type = label_t; + } else if ( addr->is_shared() ) { + m_type = symbolic_t; + } else if ( addr->is_const() ) { + m_type = symbolic_t; + } else if ( addr->is_global() ) { + m_type = symbolic_t; + } else if ( addr->is_local() ) { + m_type = symbolic_t; + } else if ( addr->is_tex() ) { + m_type = symbolic_t; + } else if ( addr->is_func_addr() ) { + m_type = symbolic_t; + } else { + m_type = reg_t; + } + m_value.m_symbolic = addr; + m_addr_offset = 0; + m_vector = false; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( int builtin_id, int dim_mod ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = builtin_t; + m_value.m_int = builtin_id; + m_addr_offset = dim_mod; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( const symbol *addr, int offset ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = address_t; + m_value.m_symbolic = addr; + m_addr_offset = offset; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( unsigned x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = unsigned_t; + m_value.m_unsigned = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( int x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = int_t; + m_value.m_int = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( float x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = float_op_t; + m_value.m_float = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( double x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = double_op_t; + m_value.m_double = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( const symbol *s1, const symbol *s2, const symbol *s3, const symbol *s4 ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = true; + m_type = vector_t; + m_value.m_vector_symbolic = new const symbol*[4]; + m_value.m_vector_symbolic[0] = s1; + m_value.m_vector_symbolic[1] = s2; + m_value.m_vector_symbolic[2] = s3; + m_value.m_vector_symbolic[3] = s4; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + + void make_memory_operand() { m_type = memory_t;} + void set_return() { m_is_return_var = true; } + + const std::string &name() const + { + assert( m_type == symbolic_t || m_type == reg_t || m_type == address_t || m_type == memory_t || m_type == label_t); + return m_value.m_symbolic->name(); + } + + unsigned get_vect_nelem() const + { + assert( is_vector() ); + if( !m_value.m_vector_symbolic[0] ) return 0; + if( !m_value.m_vector_symbolic[1] ) return 1; + if( !m_value.m_vector_symbolic[2] ) return 2; + if( !m_value.m_vector_symbolic[3] ) return 3; + return 4; + } + const std::string &vec_name1() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[0]->name(); + } + + const std::string &vec_name2() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[1]->name(); + } + + const std::string &vec_name3() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[2]->name(); + } + + const std::string &vec_name4() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[3]->name(); + } + + bool is_reg() const + { + if ( m_type == reg_t ) { + return true; + } + if ( m_type != symbolic_t ) { + return false; + } + return m_value.m_symbolic->type()->get_key().is_reg(); + } + + bool is_vector() const + { + if ( m_vector) return true; + return false; + } + int reg_num() const { return m_value.m_symbolic->reg_num();} + int reg1_num() const { return m_value.m_vector_symbolic[0]->reg_num();} + int reg2_num() const { return m_value.m_vector_symbolic[1]->reg_num();} + int reg3_num() const { return m_value.m_vector_symbolic[2]?m_value.m_vector_symbolic[2]->reg_num():0; } + int reg4_num() const { return m_value.m_vector_symbolic[3]?m_value.m_vector_symbolic[3]->reg_num():0; } + int arch_reg_num() const { return m_value.m_symbolic->arch_reg_num(); } + int arch_reg_num(unsigned n) const { return (m_value.m_vector_symbolic[n])? m_value.m_vector_symbolic[n]->arch_reg_num() : -1; } + bool is_label() const { return m_type == label_t;} + bool is_builtin() const { return m_type == builtin_t;} + bool is_memory_operand() const { return m_type == memory_t;} + bool is_literal() const { return m_type == int_t || + m_type == float_op_t || + m_type == double_op_t || + m_type == unsigned_t;} + bool is_shared() const { + if ( !(m_type == symbolic_t || m_type == address_t || m_type == memory_t) ) { + return false; + } + return m_value.m_symbolic->is_shared(); + } + bool is_const() const { return m_value.m_symbolic->is_const();} + bool is_global() const { return m_value.m_symbolic->is_global();} + bool is_local() const { return m_value.m_symbolic->is_local();} + bool is_tex() const { return m_value.m_symbolic->is_tex();} + bool is_return_var() const { return m_is_return_var; } + + bool is_function_address() const + { + if( m_type != symbolic_t ) { + return false; + } + return m_value.m_symbolic->is_func_addr(); + } + + ptx_reg_t get_literal_value() const + { + ptx_reg_t result; + switch ( m_type ) { + case int_t: result.s32 = m_value.m_int; break; + case float_op_t: result.f32 = m_value.m_float; break; + case double_op_t: result.f64 = m_value.m_double; break; + case unsigned_t: result.u32 = m_value.m_unsigned; break; + default: + assert(0); + break; + } + return result; + } + int get_int() const { return m_value.m_int;} + int get_addr_offset() const { return m_addr_offset;} + const symbol *get_symbol() const { return m_value.m_symbolic;} + void set_type( enum operand_type type ) + { + m_type = type; + } + enum operand_type get_type() const { + return m_type; + } + void set_neg_pred() + { + assert( m_valid ); + m_neg_pred = true; + } + bool is_neg_pred() const { return m_neg_pred; } + bool is_valid() const { return m_valid; } + +private: + unsigned m_uid; + bool m_valid; + bool m_vector; + enum operand_type m_type; + + union { + int m_int; + unsigned int m_unsigned; + float m_float; + double m_double; + int m_vint[4]; + unsigned int m_vunsigned[4]; + float m_vfloat[4]; + double m_vdouble[4]; + const symbol* m_symbolic; + const symbol** m_vector_symbolic; + } m_value; + + int m_addr_offset; + + bool m_neg_pred; + bool m_is_return_var; + + static unsigned sm_next_uid; + unsigned get_uid(); +}; + +extern const char *g_opcode_string[]; +extern unsigned g_num_ptx_inst_uid; +struct basic_block_t { + basic_block_t( unsigned ID, ptx_instruction *begin, ptx_instruction *end, bool entry, bool ex) + { + bb_id = ID; + ptx_begin = begin; + ptx_end = end; + is_entry=entry; + is_exit=ex; + immediatepostdominator_id = -1; + } + + ptx_instruction* ptx_begin; + ptx_instruction* ptx_end; + std::set<int> predecessor_ids; //indices of other basic blocks in m_basic_blocks array + std::set<int> successor_ids; + std::set<int> postdominator_ids; + std::set<int> dominator_ids; + std::set<int> Tmp_ids; + int immediatepostdominator_id; + bool is_entry; + bool is_exit; + unsigned bb_id; +}; + +struct gpgpu_recon_t { + address_type source_pc; + address_type target_pc; +}; + +class ptx_instruction { +public: + ptx_instruction( int opcode, + const symbol *pred, + int neg_pred, + symbol *label, + const std::list<operand_info> &operands, + const operand_info &return_var, + const std::list<int> &options, + const std::list<int> &scalar_type, + int space_spec, + const char *file, + unsigned line, + const char *source ); + + void print_insn() const; + void print_insn( FILE *fp ) const; + unsigned uid() const { return m_uid;} + int get_opcode() { return m_opcode;} + const char *get_opcode_cstr() const + { + if ( m_opcode != -1 ) { + return g_opcode_string[m_opcode]; + } else { + return "label"; + } + } + const char *source_file() const { return m_source_file.c_str();} + unsigned source_line() const { return m_source_line;} + unsigned get_num_operands() const { return m_operands.size();} + bool has_pred() const { return m_pred != NULL;} + operand_info get_pred() const { return operand_info( m_pred );} + bool get_pred_neg() const { return m_neg_pred;} + const char *get_source() const { return m_source.c_str();} + + typedef std::vector<operand_info>::const_iterator const_iterator; + + const_iterator op_iter_begin() const + { + return m_operands.begin(); + } + + const_iterator op_iter_end() const + { + return m_operands.end(); + } + + const operand_info &dst() const + { + assert( !m_operands.empty() ); + return m_operands[0]; + } + + const operand_info &func_addr() const + { + assert( !m_operands.empty() ); + if( !m_operands[0].is_return_var() ) { + return m_operands[0]; + } else { + assert( m_operands.size() >= 2 ); + return m_operands[1]; + } + } + + operand_info &dst() + { + assert( !m_operands.empty() ); + return m_operands[0]; + } + + const operand_info &src1() const + { + assert( m_operands.size() > 1 ); + return m_operands[1]; + } + + const operand_info &src2() const + { + assert( m_operands.size() > 2 ); + return m_operands[2]; + } + + const operand_info &src3() const + { + assert( m_operands.size() > 3 ); + return m_operands[3]; + } + + const operand_info &operand_lookup( unsigned n ) const + { + assert( n < m_operands.size() ); + return m_operands[n]; + } + bool has_return() const + { + return m_return_var.is_valid(); + } + + unsigned get_space() const { return m_space_spec;} + unsigned get_vector() const { return m_vector_spec;} + unsigned get_atomic() const { return m_atomic_spec;} + + int get_type() const + { + assert( !m_scalar_type.empty() ); + return m_scalar_type.front(); + } + + int get_type2() const + { + assert( m_scalar_type.size()==2 ); + return m_scalar_type.back(); + } + + void assign_bb(basic_block_t* basic_block) //assign instruction to a basic block + { + m_basic_block = basic_block; + } + basic_block_t* get_bb() { return m_basic_block;} + void set_m_instr_mem_index(unsigned index) { + m_instr_mem_index = index; + } + void set_PC( addr_t PC ) + { + m_PC = PC; + } + addr_t get_PC() const + { + return m_PC; + } + + unsigned get_m_instr_mem_index() { return m_instr_mem_index;} + unsigned get_cmpop() const { return m_compare_op;} + const symbol *get_label() const { return m_label;} + bool is_label() const { if(m_label){ assert(m_opcode==-1);return true;} return false;} + bool is_hi() const { return m_hi;} + bool is_lo() const { return m_lo;} + bool is_wide() const { return m_wide;} + bool is_uni() const { return m_uni;} + unsigned rounding_mode() const { return m_rounding_mode;} + unsigned saturation_mode() const { return m_saturation_mode;} + unsigned dimension() const { return m_geom_spec;} + enum vote_mode_t { vote_any, vote_all, vote_uni }; + enum vote_mode_t vote_mode() const { return m_vote_mode; } + + unsigned warp_size() const { return m_warp_size; } + int membar_level() const { return m_membar_level; } +private: + basic_block_t *m_basic_block; + unsigned m_uid; + addr_t m_PC; + std::string m_source_file; + unsigned m_source_line; + std::string m_source; + unsigned m_warp_size; + + const symbol *m_pred; + bool m_neg_pred; + int m_opcode; + const symbol *m_label; + std::vector<operand_info> m_operands; + operand_info m_return_var; + + std::list<int> m_options; + bool m_wide; + bool m_hi; + bool m_lo; + bool m_uni; //if branch instruction, this evaluates to true for uniform branches (ie jumps) + unsigned m_rounding_mode; + unsigned m_compare_op; + unsigned m_saturation_mode; + + std::list<int> m_scalar_type; + int m_space_spec; + int m_geom_spec; + int m_vector_spec; + int m_atomic_spec; + enum vote_mode_t m_vote_mode; + int m_membar_level; + int m_instr_mem_index; //index into m_instr_mem array +}; + +class param_info { +public: + param_info() { m_valid = false; m_value_set=false;} + param_info( unsigned index, std::string name, int type ) + { + m_valid = true; + m_value_set = false; + m_index = index; + m_name = name; + m_type = type; + } + void add_data( param_t v ) { + m_value_set = true; + m_value = v; + } + std::string get_name() const { return m_name; } + int get_type() const { return m_type; } + param_t get_value() const { assert(m_value_set); return m_value; } +private: + bool m_valid; + unsigned m_index; + std::string m_name; + int m_type; + bool m_value_set; + param_t m_value; +}; + +class function_info { +public: + function_info(int entry_point ) + { + m_entry_point = entry_point?true:false; + num_reconvergence_pairs = 0; + m_symtab = NULL; + m_assembled = false; + m_return_var_sym = NULL; + m_kernel_info.cmem = 0; + m_kernel_info.lmem = 0; + m_kernel_info.regs = 0; + m_kernel_info.smem = 0; + } + void set_name(const char *name) + { + m_name = name; + } + void set_symtab(symbol_table *symtab ) + { + m_symtab = symtab; + } + std::string get_name() + { + return m_name; + } + void print_insn( unsigned pc, FILE * fp ) const; + void add_inst( const std::list<ptx_instruction*> &instructions ) + { + m_instructions = instructions; + } + std::list<ptx_instruction*>::iterator find_next_real_instruction( std::list<ptx_instruction*>::iterator i ); + void create_basic_blocks( ); + + void print_basic_blocks(); + + void print_basic_block_links(); + void print_basic_block_dot(); + + void connect_basic_blocks( ); //iterate across m_basic_blocks of function, connecting basic blocks together + + //iterate across m_basic_blocks of function, + //finding postdominator blocks, using algorithm of + //Muchnick's Adv. Compiler Design & Implemmntation Fig 7.14 + void find_postdominators( ); + + //iterate across m_basic_blocks of function, + //finding immediate postdominator blocks, using algorithm of + //Muchnick's Adv. Compiler Design & Implemmntation Fig 7.15 + void find_ipostdominators( ); + + void print_postdominators(); + + void print_ipostdominators(); + + unsigned get_num_reconvergence_pairs(); + + void get_reconvergence_pairs(gpgpu_recon_t* recon_points); + + unsigned get_function_size() { return m_instructions.size();} + + void ptx_assemble(); + void ptx_decode_inst( ptx_thread_info *thd, + unsigned *op_type, + int *i1, + int *i2, + int *i3, + int *i4, + int *o1, + int *o2, + int *o3, + int *o4, + int *vectorin, + int *vectorout, + int *arch_reg ); + unsigned ptx_get_inst_op( ptx_thread_info *thread ); + void ptx_exec_inst( ptx_thread_info *thd, addr_t *addr, unsigned *space, unsigned *data_size, dram_callback_t* callback, unsigned warp_active_mask ); + void add_param( const char *name, struct param_t value ) + { + m_params[ name ] = value; + } + void add_param_name_and_type( unsigned index, std::string name, int type ); + void add_param_data( unsigned argn, struct gpgpu_ptx_sim_arg *args ); + void add_return_var( const symbol *rv ) + { + m_return_var_sym = rv; + } + void add_arg( const symbol *arg ) + { + assert( arg != NULL ); + m_args.push_back(arg); + } + void remove_args() + { + m_args.clear(); + } + unsigned num_args() const + { + return m_args.size(); + } + const symbol* get_arg( unsigned n ) const + { + assert( n < m_args.size() ); + return m_args[n]; + } + bool has_return() const + { + return m_return_var_sym != NULL; + } + const symbol *get_return_var() const + { + return m_return_var_sym; + } + const ptx_instruction *get_instruction( unsigned PC ) const + { + unsigned index = PC - m_start_PC; + if( index < m_instr_mem_size ) + return m_instr_mem[index]; + return NULL; + } + addr_t get_start_PC() const + { + return m_start_PC; + } + + void finalize( memory_space *param_mem, symbol_table *symtab ) + { + unsigned param_address = 0; + for( std::map<unsigned,param_info>::iterator i=m_ptx_param_info.begin(); i!=m_ptx_param_info.end(); i++ ) { + param_info &p = i->second; + std::string name = p.get_name(); + int type = p.get_type(); + param_t value = p.get_value(); + value.type = type; + symbol *param = symtab->lookup(name.c_str()); + unsigned xtype = param->type()->get_key().scalar_type(); + assert(xtype==(unsigned)type); + int tmp; + size_t size; + type_decode(xtype,size,tmp); + param_mem->write(param_address,size/8,&value); + param->set_address(param_address); + param_address += 8;//align to 64 bits so mem_access doesn't complain (before was size/8); + } + } + ptx_reg_t get_param( const std::string &name ) const + { + std::map<std::string,param_t>::const_iterator i = m_params.find(name); + if ( i == m_params.end() ) { + printf("Loader error: parameter \"%s\" value not defined in configuration\n", name.c_str() ); + abort(); + } else { + param_t x = i->second; + ptx_reg_t y; + switch ( x.type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case B8_TYPE: + case B16_TYPE: + case B32_TYPE: + case B64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + y.u64 = x.data.int_value; + break; + case F16_TYPE: + assert(0); + case F32_TYPE: + y.f32 = x.data.float_value; + break; + case F64_TYPE: + y.f64 = x.data.double_value; + break; + } + return y; + } + } + + const struct gpgpu_ptx_sim_kernel_info* get_kernel_info () { + return &m_kernel_info; + } + + const void set_kernel_info (const struct gpgpu_ptx_sim_kernel_info *info) { + m_kernel_info = *info; + } + symbol_table *get_symtab() + { + return m_symtab; + } + + static const ptx_instruction* pc_to_instruction(unsigned pc) + { + assert(pc > 0); + assert(pc <= s_g_pc_to_insn.size()); + return s_g_pc_to_insn[pc - 1]; + } + +private: + bool m_entry_point; + bool m_assembled; + std::string m_name; + ptx_instruction **m_instr_mem; + unsigned m_start_PC; + unsigned m_instr_mem_size; + std::map<std::string,param_t> m_params; + std::map<unsigned,param_info> m_ptx_param_info; + const symbol *m_return_var_sym; + std::vector<const symbol*> m_args; + std::list<ptx_instruction*> m_instructions; + std::vector<basic_block_t*> m_basic_blocks; + std::list<std::pair<unsigned, unsigned> > m_back_edges; + std::map<std::string,unsigned> labels; + unsigned num_reconvergence_pairs; + + //Registers/shmem/etc. used (from ptxas -v), loaded from ___.ptxinfo along with ___.ptx + struct gpgpu_ptx_sim_kernel_info m_kernel_info; + + symbol_table *m_symtab; + + static std::vector<ptx_instruction*> s_g_pc_to_insn; // a direct mapping from PC to instruction +}; + +/*******************************/ +// These declarations should be identical to those in ./../../cuda-sim-dev/libcuda/texture_types.h +enum cudaChannelFormatKind { + cudaChannelFormatKindSigned, + cudaChannelFormatKindUnsigned, + cudaChannelFormatKindFloat +}; + +struct cudaChannelFormatDesc { + int x; + int y; + int z; + int w; + enum cudaChannelFormatKind f; +}; + +struct cudaArray { + void *devPtr; + int devPtr32; + struct cudaChannelFormatDesc desc; + int width; + int height; + int size; //in bytes + unsigned dimensions; +}; + +enum cudaTextureAddressMode { + cudaAddressModeWrap, + cudaAddressModeClamp +}; + +enum cudaTextureFilterMode { + cudaFilterModePoint, + cudaFilterModeLinear +}; + +enum cudaTextureReadMode { + cudaReadModeElementType, + cudaReadModeNormalizedFloat +}; + +struct textureReference { + int normalized; + enum cudaTextureFilterMode filterMode; + enum cudaTextureAddressMode addressMode[2]; + struct cudaChannelFormatDesc channelDesc; +}; + +/**********************************/ + +struct textureInfo { + unsigned int texel_size; //size in bytes, e.g. (channelDesc.x+y+z+w)/8 + unsigned int Tx,Ty; //tiling factor dimensions of layout of texels per 64B cache block + unsigned int Tx_numbits,Ty_numbits; //log2(T) + unsigned int texel_size_numbits; //log2(texel_size) +}; + + +extern function_info *g_func_info; + +extern int g_error_detected; +extern bool g_debug_ir_generation; +extern std::list<ptx_instruction*> g_instructions; +extern symbol_table *g_current_symbol_table; +extern symbol_table *g_entrypoint_symbol_table; +extern function_info *g_entrypoint_func_info; +extern symbol_table *g_global_symbol_table; +void init_parser(); + +extern "C" { +#endif + + void start_function( int entry_point ); + void add_function_name( const char *fname ); + void init_directive_state(); + void add_directive(); + void end_function(); + void add_identifier( const char *s, int array_dim, unsigned array_ident ); + void add_function_arg(); + void add_scalar_type_spec( int type_spec ); + void add_scalar_operand( const char *identifier ); + void add_neg_pred_operand( const char *identifier ); + void add_variables(); + void set_variable_type(); + void add_opcode( int opcode ); + void add_pred( const char *identifier, int negate ); + void add_2vector_operand( const char *d1, const char *d2 ); + void add_3vector_operand( const char *d1, const char *d2, const char *d3 ); + void add_4vector_operand( const char *d1, const char *d2, const char *d3, const char *d4 ); + void add_option(int option ); + void add_builtin_operand( int builtin, int dim_modifier ); + void add_memory_operand( ); + void add_literal_int( int value ); + void add_literal_float( float value ); + void add_literal_double( double value ); + void add_address_operand( const char *identifier, int offset ); + void add_label( const char *idenfiier ); + void add_vector_spec(int spec ); + void add_space_spec(int spec ); + void add_extern_spec(); + void add_instruction(); + void set_return(); + void add_alignment_spec( int spec ); + void add_array_initializer(); + void add_file( unsigned num, const char *filename ); + void *reset_symtab(); + void set_symtab(void*); + + +#define NON_ARRAY_IDENTIFIER 1 +#define ARRAY_IDENTIFIER_NO_DIM 2 +#define ARRAY_IDENTIFIER 3 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cuda-sim/ptx_sim.cc b/src/cuda-sim/ptx_sim.cc new file mode 100644 index 0000000..8ec60df --- /dev/null +++ b/src/cuda-sim/ptx_sim.cc @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Ali Bakhoda, George L. Yuan, + * Dan O'Connor, and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "ptx_sim.h" +#include <string> +#include "ptx_ir.h" + +std::set<unsigned long long> g_ptx_cta_info_sm_idx_used; +unsigned long long g_ptx_cta_info_uid = 1; +extern int gpgpu_option_spread_blocks_across_cores; + +ptx_cta_info::ptx_cta_info( unsigned sm_idx ) +{ + assert( g_ptx_cta_info_sm_idx_used.find(sm_idx) == g_ptx_cta_info_sm_idx_used.end() ); + g_ptx_cta_info_sm_idx_used.insert(sm_idx); + + m_sm_idx = sm_idx; + m_uid = g_ptx_cta_info_uid++; +} + +void ptx_cta_info::add_thread( ptx_thread_info *thd ) +{ + m_threads_in_cta.insert(thd); +} + +void ptx_cta_info::add_to_barrier( ptx_thread_info *thd ) +{ + m_threads_waiting_at_barrier.insert(thd); +} + +void ptx_cta_info::release_barrier() +{ + assert( m_threads_waiting_at_barrier.size() == m_threads_in_cta.size() ); + m_threads_waiting_at_barrier.clear(); +} + +bool ptx_cta_info::all_at_barrier() const +{ + return(m_threads_waiting_at_barrier.size() == m_threads_in_cta.size()); +} + +unsigned ptx_cta_info::num_threads() const +{ + return m_threads_in_cta.size(); +} + +void ptx_cta_info::check_cta_thread_status_and_reset() +{ + bool fail = false; + if ( m_threads_that_have_exited.size() != m_threads_in_cta.size() ) { + printf("\n\n"); + printf("Execution error: Some threads still running in CTA during CTA reallocation! (1)\n"); + printf(" CTA uid = %Lu (sm_idx = %u) : %lu running out of %zu total\n", + m_uid, + m_sm_idx, + (m_threads_in_cta.size() - m_threads_that_have_exited.size()), m_threads_in_cta.size() ); + printf(" These are the threads that are still running:\n"); + std::set<ptx_thread_info*>::iterator t_iter; + for ( t_iter=m_threads_in_cta.begin(); t_iter != m_threads_in_cta.end(); ++t_iter ) { + ptx_thread_info *t = *t_iter; + if ( m_threads_that_have_exited.find(t) == m_threads_that_have_exited.end() ) { + if ( m_dangling_pointers.find(t) != m_dangling_pointers.end() ) { + printf(" <thread deleted>\n"); + } else { + printf(" [done=%c] : ", (t->is_done()?'Y':'N') ); + t->print_insn( t->get_pc(), stdout ); + printf("\n"); + } + } + } + printf("\n\n"); + fail = true; + } + assert_barrier_empty(true); + if ( fail ) { + abort(); + } + + bool fail2 = false; + std::set<ptx_thread_info*>::iterator t_iter; + for ( t_iter=m_threads_in_cta.begin(); t_iter != m_threads_in_cta.end(); ++t_iter ) { + ptx_thread_info *t = *t_iter; + if ( m_dangling_pointers.find(t) == m_dangling_pointers.end() ) { + if ( !t->is_done() ) { + if ( !fail2 ) { + printf("Execution error: Some threads still running in CTA during CTA reallocation! (2)\n"); + printf(" CTA uid = %Lu (sm_idx = %u) :\n", m_uid, m_sm_idx ); + fail2 = true; + } + printf(" "); + t->print_insn( t->get_pc(), stdout ); + printf("\n"); + } + } + } + if ( fail2 ) { + abort(); + } + m_threads_in_cta.clear(); + m_threads_that_have_exited.clear(); + m_dangling_pointers.clear(); +} + +void ptx_cta_info::assert_barrier_empty( bool called_from_delete_threads ) const +{ + if ( !m_threads_waiting_at_barrier.empty() ) { + printf( "\n\n" ); + printf( "Execution error: Some threads at barrier at %s:\n", + (called_from_delete_threads?"CTA re-init":"thread exit") ); + printf( " CTA uid = %Lu (sm_idx = %u)\n", m_uid, m_sm_idx ); + std::set<ptx_thread_info*>::iterator b; + for ( b=m_threads_waiting_at_barrier.begin(); b != m_threads_waiting_at_barrier.end(); ++b ) { + ptx_thread_info *t = *b; + if ( m_dangling_pointers.find(t) != m_dangling_pointers.end() ) { + printf(" <thread deleted>\n"); + } else { + printf(" "); + t->print_insn( t->get_pc(), stdout ); + printf("\n"); + } + } + printf("\n\n"); + abort(); + } +} + +void ptx_cta_info::register_thread_exit( ptx_thread_info *thd ) +{ + assert( m_threads_that_have_exited.find(thd) == m_threads_that_have_exited.end() ); + m_threads_that_have_exited.insert(thd); +} + +void ptx_cta_info::register_deleted_thread( ptx_thread_info *thd ) +{ + m_dangling_pointers.insert(thd); +} + +unsigned ptx_cta_info::get_sm_idx() const +{ + return m_sm_idx; +} + +unsigned g_ptx_thread_info_uid_next=1; +unsigned g_ptx_thread_info_delete_count=0; + +ptx_thread_info::~ptx_thread_info() +{ + g_ptx_thread_info_delete_count++; +} + +ptx_thread_info::ptx_thread_info() +{ + m_uid = g_ptx_thread_info_uid_next++; + m_core = NULL; + m_barrier_num = -1; + m_at_barrier = false; + m_valid = false; + m_gridid = 0; + m_thread_done = false; + m_cycle_done = 0; + m_PC=0; + m_icount = 0; + m_last_effective_address = 0; + m_last_memory_space = 0; + m_branch_taken = 0; + m_shared_mem = NULL; + m_cta_info = NULL; + m_local_mem = NULL; + m_symbol_table = NULL; + m_func_info = NULL; + m_hw_tid = -1; + m_hw_wid = -1; + m_hw_sid = -1; + m_last_dram_callback.function = NULL; + m_last_dram_callback.instruction = NULL; + m_regs.push_back( std::map<std::string,ptx_reg_t>() ); + m_debug_trace_regs_modified.push_back( std::map<std::string,ptx_reg_t>() ); + m_callstack.push_back( stack_entry() ); + m_RPC = -1; + m_RPC_updated = false; + m_last_was_call = false; +} + +extern unsigned long long gpu_sim_cycle; + +void ptx_thread_info::set_done() +{ + assert( !m_at_barrier ); + m_thread_done = true; + m_cycle_done = gpu_sim_cycle; +} + +extern unsigned long long gpu_sim_cycle; +extern signed long long gpu_tot_sim_cycle; + +unsigned ptx_thread_info::get_builtin( int builtin_id, unsigned dim_mod ) +{ + assert( m_valid ); + switch (builtin_id) { + case NTID_ID: + assert( dim_mod < 3 ); + return m_ntid[dim_mod]; + case CLOCK_ID: + return gpu_sim_cycle + gpu_tot_sim_cycle; + case CTA_ID: + assert( dim_mod < 3 ); + return m_ctaid[dim_mod]; + case GRIDID_ID: + return m_gridid; + case NCTAID_ID: + assert( dim_mod < 3 ); + return m_nctaid[dim_mod]; + case TID_ID: + assert( dim_mod < 3 ); + return m_tid[dim_mod]; + default: + assert(0); + } + return 0; +} + +void ptx_thread_info::set_info( symbol_table *symtab, function_info *func ) +{ + m_symbol_table = symtab; + m_func_info = func; + m_PC = func->get_start_PC(); +} + +void ptx_thread_info::print_insn( unsigned pc, FILE * fp ) const +{ + m_func_info->print_insn(pc,fp); +} + +static void print_reg( std::string name, ptx_reg_t value ) +{ + const symbol *sym = g_current_symbol_table->lookup(name.c_str()); + printf(" %8s ", name.c_str() ); + if( sym == NULL ) { + printf("<unknown type> 0x%llx\n", (unsigned long long ) value.u64 ); + return; + } + const type_info *t = sym->type(); + type_info_key ti = t->get_key(); + + switch ( ti.scalar_type() ) { + case S8_TYPE: printf(".s8 %d\n", value.s8 ); break; + case S16_TYPE: printf(".s16 %d\n", value.s16 ); break; + case S32_TYPE: printf(".s32 %d\n", value.s32 ); break; + case S64_TYPE: printf(".s64 %Ld\n", value.s64 ); break; + case U8_TYPE: printf(".u8 0x%02x\n", (unsigned) value.u8 ); break; + case U16_TYPE: printf(".u16 0x%04x\n", (unsigned) value.u16 ); break; + case U32_TYPE: printf(".u32 0x%08x\n", (unsigned) value.u32 ); break; + case U64_TYPE: printf(".u64 0x%llx\n", value.u64 ); break; + case F16_TYPE: printf(".f16 %f [0x%04x]\n", value.f16, (unsigned) value.u16 ); break; + case F32_TYPE: printf(".f32 %.15lf [0x%08x]\n", value.f32, value.u32 ); break; + case F64_TYPE: printf(".f64 %.15le [0x%016llx]\n", value.f64, value.u64 ); break; + case B8_TYPE: printf(".b8 0x%02x\n", (unsigned) value.u8 ); break; + case B16_TYPE: printf(".b16 0x%04x\n", (unsigned) value.u16 ); break; + case B32_TYPE: printf(".b32 0x%08x\n", (unsigned) value.u32 ); break; + case B64_TYPE: printf(".b64 0x%llx\n", (unsigned long long ) value.u64 ); break; + case PRED_TYPE: printf(".pred %u\n", (unsigned) value.pred ); break; + default: + printf( "non-scalar type\n" ); + break; + } +} + +void ptx_thread_info::callstack_push( unsigned pc, unsigned rpc, const symbol *return_var_src, const symbol *return_var_dst, unsigned call_uid ) +{ + m_RPC = -1; + m_RPC_updated = true; + m_last_was_call = true; + m_callstack.push_back( stack_entry(m_symbol_table,m_func_info,pc,rpc,return_var_src,return_var_dst,call_uid) ); + m_regs.push_back( std::map<std::string,ptx_reg_t>() ); + m_debug_trace_regs_modified.push_back( std::map<std::string,ptx_reg_t>() ); +} + +#define POST_DOMINATOR 1 /* must match definition in shader.h */ +extern int gpgpu_simd_model; +extern ptx_reg_t get_operand_value( const symbol *reg ); +extern void set_operand_value( const symbol *dst, const ptx_reg_t &data ); + +bool ptx_thread_info::callstack_pop() +{ + assert( !m_callstack.empty() ); + m_func_info = m_callstack.back().m_func_info; + m_symbol_table = m_callstack.back().m_symbol_table; + m_NPC = m_callstack.back().m_PC; + m_RPC_updated = true; + m_last_was_call = false; + m_RPC = m_callstack.back().m_RPC; + const symbol *rv_src = m_callstack.back().m_return_var_src; + const symbol *rv_dst = m_callstack.back().m_return_var_dst; + assert( !((rv_src != NULL) ^ (rv_dst != NULL)) ); + ptx_reg_t rval; + if( rv_src != NULL ) + rval = get_operand_value(rv_src); + m_callstack.pop_back(); + m_regs.pop_back(); + m_debug_trace_regs_modified.pop_back(); + if( rv_dst != NULL ) + set_operand_value(rv_dst,rval); + return m_callstack.empty(); +} + +void ptx_thread_info::dump_callstack() const +{ + std::list<stack_entry>::const_iterator c=m_callstack.begin(); + std::list<std::map<std::string,ptx_reg_t> >::const_iterator r=m_regs.begin(); + + printf("\n\n"); + printf("Call stack for thread uid = %u (sc=%u, hwtid=%u)\n", m_uid, m_hw_sid, m_hw_tid ); + while( c != m_callstack.end() && r != m_regs.end() ) { + const stack_entry &c_e = *c; + const std::map<std::string,ptx_reg_t> ®s = *r; + if( !c_e.m_valid ) { + printf(" <entry> #regs = %zu\n", regs.size() ); + } else { + printf(" %20s PC=%3u RV= (callee=\'%s\',caller=\'%s\') #regs = %zu\n", + c_e.m_func_info->get_name().c_str(), c_e.m_PC, + c_e.m_return_var_src->name().c_str(), + c_e.m_return_var_dst->name().c_str(), + regs.size() ); + } + c++; + r++; + } + if( c != m_callstack.end() || r != m_regs.end() ) { + printf(" *** mismatch in m_regs and m_callstack sizes ***\n" ); + } + printf("\n\n"); +} + +std::string ptx_thread_info::get_location() const +{ + const ptx_instruction *pI = m_func_info->get_instruction(m_PC); + char buf[1024]; + snprintf(buf,1024,"%s:%u", pI->source_file(), pI->source_line() ); + return std::string(buf); +} + +void ptx_thread_info::dump_regs() +{ + printf("Register File Contents:\n"); + std::map<std::string,ptx_reg_t>::const_iterator r; + for ( r=m_regs.back().begin(); r != m_regs.back().end(); ++r ) { + std::string name = r->first; + ptx_reg_t value = r->second; + print_reg(name,value); + + } +} + +void ptx_thread_info::dump_modifiedregs() +{ + if( m_debug_trace_regs_modified.empty() ) + return; + printf("Modified Registers:\n"); + std::map<std::string,ptx_reg_t>::const_iterator r; + for ( r=m_debug_trace_regs_modified.back().begin(); r != m_debug_trace_regs_modified.back().end(); ++r ) { + std::string name = r->first; + ptx_reg_t value = r->second; + print_reg(name,value); + } +} + +void ptx_thread_info::set_npc( const function_info *f ) +{ + m_NPC = f->get_start_PC(); + m_func_info = const_cast<function_info*>( f ); + m_symbol_table = m_func_info->get_symtab(); +} diff --git a/src/cuda-sim/ptx_sim.h b/src/cuda-sim/ptx_sim.h new file mode 100644 index 0000000..17eb250 --- /dev/null +++ b/src/cuda-sim/ptx_sim.h @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Dan O'Connor, Henry Wong and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef ptx_sim_h_INCLUDED +#define ptx_sim_h_INCLUDED + +#include <stdlib.h> + +#include "dram_callback.h" +#include "../util.h" +#include "../abstract_hardware_model.h" + +typedef address_type addr_t; + +struct dim3 { + unsigned int x, y, z; +}; + +struct gpgpu_ptx_sim_arg { + const void *m_start; + size_t m_nbytes; + size_t m_offset; + struct gpgpu_ptx_sim_arg *m_next; +}; + +//Holds properties of the kernel (Kernel's resource use). These will be zero if +//the ptxinfo file is not present. +struct gpgpu_ptx_sim_kernel_info { + int lmem; + int smem; + int cmem; + int regs; +}; + +#include <assert.h> +#include "opcodes.h" + +struct param_t { + union { + float float_value; + int int_value; + double double_value; + unsigned long long ptr_value; + } data; + int type; +}; + +#ifdef __cplusplus + + #include <string> + #include <map> + #include <set> + #include <list> + +#include "memory.h" + +union ptx_reg_t { + ptx_reg_t() { + bits.ms = 0; + bits.ls = 0; + } + ptx_reg_t(unsigned x) + { + bits.ms = 0; + bits.ls = 0; + u32 = x; + } + operator unsigned int() { return u32;} + operator unsigned short() { return u16;} + operator unsigned char() { return u8;} + operator unsigned long long() { return u64;} + + void mask_and( unsigned ms, unsigned ls ) + { + bits.ms &= ms; + bits.ls &= ls; + } + + void mask_or( unsigned ms, unsigned ls ) + { + bits.ms |= ms; + bits.ls |= ls; + } + int get_bit( unsigned bit ) + { + if ( bit < 32 ) + return(bits.ls >> bit) & 1; + else + return(bits.ms >> (bit-32)) & 1; + } + + signed char s8; + signed short s16; + signed int s32; + signed long long s64; + unsigned char u8; + unsigned short u16; + unsigned int u32; + unsigned long long u64; + float f16; + float f32; + double f64; + struct { + unsigned ls; + unsigned ms; + } bits; + unsigned pred : 1; + +}; + +class ptx_instruction; +class operand_info; +class symbol_table; +class function_info; +class ptx_thread_info; + +class ptx_cta_info { +public: + ptx_cta_info( unsigned sm_idx ); + void add_thread( ptx_thread_info *thd ); + void add_to_barrier( ptx_thread_info *thd ); + bool all_at_barrier() const; + void release_barrier(); + unsigned num_threads() const; + void check_cta_thread_status_and_reset(); + void assert_barrier_empty( bool called_from_delete_threads = false ) const; + void register_thread_exit( ptx_thread_info *thd ); + void register_deleted_thread( ptx_thread_info *thd ); + unsigned get_sm_idx() const; + +private: + unsigned long long m_uid; + unsigned m_sm_idx; + std::set<ptx_thread_info*> m_threads_in_cta; + std::set<ptx_thread_info*> m_threads_waiting_at_barrier; + std::set<ptx_thread_info*> m_threads_that_have_exited; + std::set<ptx_thread_info*> m_dangling_pointers; +}; + +class symbol; + +struct stack_entry { + stack_entry() { + m_symbol_table=NULL; + m_func_info=NULL; + m_PC=0; + m_RPC=-1; + m_return_var_src = NULL; + m_return_var_dst = NULL; + m_call_uid = 0; + m_valid = false; + } + stack_entry( symbol_table *s, function_info *f, unsigned pc, unsigned rpc, const symbol *return_var_src, const symbol *return_var_dst, unsigned call_uid ) + { + m_symbol_table=s; + m_func_info=f; + m_PC=pc; + m_RPC=rpc; + m_return_var_src = return_var_src; + m_return_var_dst = return_var_dst; + m_call_uid = call_uid; + m_valid = true; + } + + bool m_valid; + symbol_table *m_symbol_table; + function_info *m_func_info; + unsigned m_PC; + unsigned m_RPC; + const symbol *m_return_var_src; + const symbol *m_return_var_dst; + unsigned m_call_uid; +}; + +class ptx_thread_info { +public: + ~ptx_thread_info(); + ptx_thread_info(); + + ptx_reg_t get_operand_value( const symbol *reg ); + ptx_reg_t get_operand_value( const operand_info &op ); + void set_operand_value( const operand_info &dst, const ptx_reg_t &data ); + void set_operand_value( const symbol *dst, const ptx_reg_t &data ); + void get_vector_operand_values( const operand_info &op, ptx_reg_t* ptx_regs, unsigned num_elements ); + void set_vector_operand_values( const operand_info &dst, + const ptx_reg_t &data1, + const ptx_reg_t &data2, + const ptx_reg_t &data3, + const ptx_reg_t &data4, + unsigned num_elements ); + + function_info *func_info() + { + return m_func_info; + } + void print_insn( unsigned pc, FILE * fp ) const; + void set_info( symbol_table *symtab, function_info *func ); + unsigned get_uid() const + { + return m_uid; + } + + dim3 get_ctaid() const + { + dim3 r; + r.x = m_ctaid[0]; + r.y = m_ctaid[1]; + r.z = m_ctaid[2]; + return r; + } + dim3 get_tid() const + { + dim3 r; + r.x = m_tid[0]; + r.y = m_tid[1]; + r.z = m_tid[2]; + return r; + } + unsigned get_hw_tid() const { return m_hw_tid;} + unsigned get_hw_ctaid() const { return m_hw_ctaid;} + unsigned get_hw_wid() const { return m_hw_wid;} + unsigned get_hw_sid() const { return m_hw_sid;} + void set_hw_tid(unsigned tid) { m_hw_tid=tid;} + void set_hw_wid(unsigned wid) { m_hw_wid=wid;} + void set_hw_sid(unsigned sid) { m_hw_sid=sid;} + void set_hw_ctaid(unsigned cta_id) { m_hw_ctaid=cta_id;} + void set_core(core_t *core) { m_core = core; } + core_t *get_core() { return m_core; } + + unsigned get_icount() const { return m_icount;} + void set_valid() { m_valid = true;} + addr_t last_eaddr() const { return m_last_effective_address;} + unsigned last_space() const { return m_last_memory_space;} + dram_callback_t last_callback() const { return m_last_dram_callback;} + void set_at_barrier( int barrier_num ) + { + m_barrier_num = barrier_num; + m_at_barrier = true; + m_cta_info->add_to_barrier(this); + } + bool is_at_barrier() const { return m_at_barrier;} + bool all_at_barrier() const { return m_cta_info->all_at_barrier();} + unsigned long long get_cta_uid() { return m_cta_info->get_sm_idx();} + void clear_barrier( ) + { + m_barrier_num = -1; + m_at_barrier = false; + } + void release_barrier() { m_cta_info->release_barrier();} + + void set_single_thread_single_block() + { + m_ntid[0] = 1; + m_ntid[1] = 1; + m_ntid[2] = 1; + m_ctaid[0] = 0; + m_ctaid[1] = 0; + m_ctaid[2] = 0; + m_tid[0] = 0; + m_tid[1] = 0; + m_tid[2] = 0; + m_nctaid[0] = 1; + m_nctaid[1] = 1; + m_nctaid[2] = 1; + m_gridid = 0; + m_valid = true; + } + void set_tid( int x, int y, int z) + { + m_tid[0] = x; + m_tid[1] = y; + m_tid[2] = z; + } + void set_ctaid( int x, int y, int z) + { + m_ctaid[0] = x; + m_ctaid[1] = y; + m_ctaid[2] = z; + } + void set_ntid( int x, int y, int z) + { + m_ntid[0] = x; + m_ntid[1] = y; + m_ntid[2] = z; + } + void set_nctaid( int x, int y, int z) + { + m_nctaid[0] = x; + m_nctaid[1] = y; + m_nctaid[2] = z; + } + + unsigned get_builtin( int builtin_id, unsigned dim_mod ); + + void set_done(); + bool is_done() { return m_thread_done;} + unsigned donecycle() const { return m_cycle_done; } + + unsigned next_instr() + { + m_NPC = m_PC+1; // increment to next instruction in case of no branch + m_icount++; + m_branch_taken = false; + return m_PC; + } + bool branch_taken() const + { + return m_branch_taken; + } + unsigned get_pc() const + { + return m_PC; + } + void set_npc( unsigned npc ) + { + m_NPC = npc; + } + void set_npc( const function_info *f ); + void callstack_push( unsigned npc, unsigned rpc, const symbol *return_var_src, const symbol *return_var_dst, unsigned call_uid ); + bool callstack_pop(); + void dump_callstack() const; + std::string get_location() const; + bool rpc_updated() const { return m_RPC_updated; } + bool last_was_call() const { return m_last_was_call; } + unsigned get_rpc() const { return m_RPC; } + void clearRPC() + { + m_RPC = -1; + m_RPC_updated = false; + m_last_was_call = false; + } + unsigned get_return_PC() + { + return m_callstack.back().m_PC; + } + void update_pc() + { + m_PC = m_NPC; + } + void dump_regs(); + void dump_modifiedregs(); + void clear_modifiedregs() { m_debug_trace_regs_modified.back().clear();} + function_info *get_finfo() { return m_func_info; } + +public: + addr_t m_last_effective_address; + bool m_branch_taken; + unsigned m_last_memory_space; + dram_callback_t m_last_dram_callback; + memory_space *m_shared_mem; + memory_space *m_local_mem; + ptx_cta_info *m_cta_info; + ptx_reg_t m_last_set_operand_value; + +private: + unsigned m_uid; + core_t *m_core; + bool m_valid; + unsigned m_ntid[3]; + unsigned m_tid[3]; + unsigned m_nctaid[3]; + unsigned m_ctaid[3]; + unsigned m_gridid; + bool m_thread_done; + unsigned m_hw_sid; + unsigned m_hw_tid; + unsigned m_hw_wid; + unsigned m_hw_ctaid; + + unsigned m_icount; + unsigned m_PC; + unsigned m_NPC; + unsigned m_RPC; + bool m_RPC_updated; + bool m_last_was_call; + unsigned m_cycle_done; + + int m_barrier_num; + bool m_at_barrier; + + symbol_table *m_symbol_table; + function_info *m_func_info; + + std::list<stack_entry> m_callstack; + + std::list<std::map<std::string,ptx_reg_t> > m_regs; + std::list<std::map<std::string,ptx_reg_t> > m_debug_trace_regs_modified; +}; + +unsigned type_decode( unsigned type, size_t &size, int &t ); + +#endif + +#define MAX_REG_OPERANDS 8 + +#endif diff --git a/src/cuda-sim/ptxinfo.l b/src/cuda-sim/ptxinfo.l new file mode 100644 index 0000000..3a2ab52 --- /dev/null +++ b/src/cuda-sim/ptxinfo.l @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%option noyywrap +%option yylineno +%option prefix="ptxinfo_" +%{ +#include "ptxinfo.tab.h" +#include <string.h> + +char ptxinfo_linebuf[1024]; +unsigned ptxinfo_col = 0; +#define TC ptxinfo_col+=strlen(ptxinfo_text); +%} + +%% + +"warning"[^\n]* TC; return WARNING; +"ptxas" TC; return HEADER; +"info" TC; return INFO; +"Compiling entry function" TC; return FUNC; +"Used" TC; return USED; +"registers" TC; return REGS; +"bytes" TC; return BYTES; +"lmem" TC; return LMEM; +"smem" TC; return SMEM; +"cmem" TC; return CMEM; +"line" TC; return LINE; + +[_A-Za-z$%][_0-9A-Za-z$]* TC; ptxinfo_lval.string_value = strdup(yytext); return IDENTIFIER; +[-]{0,1}[0-9]+ TC; ptxinfo_lval.int_value = atoi(yytext); return INT_OPERAND; + +"+" TC; return PLUS; +"," TC; return COMMA; +"[" TC; return LEFT_SQUARE_BRACKET; +"]" TC; return RIGHT_SQUARE_BRACKET; +":" TC; return COLON; +";" TC; return SEMICOLON; +"'" TC; return QUOTE; +" " TC; +"\t" TC; + +\n.* ptxinfo_col=0; strncpy(ptxinfo_linebuf, yytext + 1, 1024); yyless( 1 ); + +%% + +extern int g_ptxinfo_error_detected; +extern const char *g_filename; +extern const char *g_ptxinfo_filename; + +int ptxinfo_error( const char *s ) +{ + int i; + g_ptxinfo_error_detected = 1; + fflush(stdout); + if( s != NULL ) + printf("ptxas -v %s (%s:%u) Syntax error:\n\n", g_filename, g_ptxinfo_filename, ptxinfo_lineno ); + printf(" %s\n", ptxinfo_linebuf ); + printf(" "); + for( i=0; i < ptxinfo_col-1; i++ ) { + if( ptxinfo_linebuf[i] == '\t' ) printf("\t"); + else printf(" "); + } + + printf("^\n\n"); + fflush(stdout); + exit(43); + return 0; +} diff --git a/src/cuda-sim/ptxinfo.y b/src/cuda-sim/ptxinfo.y new file mode 100644 index 0000000..c9c84b8 --- /dev/null +++ b/src/cuda-sim/ptxinfo.y @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * 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 OWNERS 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. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * 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 University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%union { + int int_value; + char * string_value; +} + +%token <int_value> INT_OPERAND +%token HEADER +%token INFO +%token FUNC +%token USED +%token REGS +%token BYTES +%token LMEM +%token SMEM +%token CMEM +%token <string_value> IDENTIFIER +%token PLUS +%token COMMA +%token LEFT_SQUARE_BRACKET +%token RIGHT_SQUARE_BRACKET +%token COLON +%token SEMICOLON +%token QUOTE +%token LINE +%token WARNING + +%{ + #include <stdlib.h> + #include <string.h> + + static unsigned g_declared; + static unsigned g_system; + int ptxinfo_lex(void); + void ptxinfo_addinfo(); + void ptxinfo_function(const char *fname ); + void ptxinfo_regs( unsigned nregs ); + void ptxinfo_lmem( unsigned declared, unsigned system ); + void ptxinfo_smem( unsigned declared, unsigned system ); + void ptxinfo_cmem( unsigned nbytes, unsigned bank ); + int ptxinfo_error(const char*); +%} + +%% + +input: /* empty */ + | input line + ; + +line: HEADER INFO COLON line_info + | HEADER IDENTIFIER COMMA LINE INT_OPERAND SEMICOLON WARNING + ; + +line_info: function_name + | function_info { ptxinfo_addinfo(); } + ; + +function_name: FUNC QUOTE IDENTIFIER QUOTE { ptxinfo_function($3); } + +function_info: info + | function_info COMMA info + ; + +info: USED INT_OPERAND REGS { ptxinfo_regs($2); } + | tuple LMEM { ptxinfo_lmem(g_declared,g_system); } + | tuple SMEM { ptxinfo_smem(g_declared,g_system); } + | INT_OPERAND BYTES CMEM LEFT_SQUARE_BRACKET INT_OPERAND RIGHT_SQUARE_BRACKET { ptxinfo_cmem($1,$5); } + | INT_OPERAND BYTES LMEM { ptxinfo_lmem($1,0); } + | INT_OPERAND BYTES SMEM { ptxinfo_smem($1,0); } + | INT_OPERAND BYTES CMEM { ptxinfo_cmem($1,0); } + | INT_OPERAND REGS { ptxinfo_regs($1); } + ; + +tuple: INT_OPERAND PLUS INT_OPERAND BYTES { g_declared=$1; g_system=$3; } + +%% + + |
