summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Aamodt <[email protected]>2010-10-03 15:16:46 -0800
committerTor Aamodt <[email protected]>2010-10-03 15:16:46 -0800
commitf97c52bbaed425f5ada9758c248c9a2c2b9853dd (patch)
tree99879a71c34f52a9a6038e85a947b3ffa4bc62c2
parent5c220c406491b72054a00d1dceab222ab796f06a (diff)
1. enable L2 cache as a texture cache (also some bug fixes for L2 as regular cache)
2. update gpgpusim.config for Quadro to use L1 cache geometry from Henry's ISPASS paper 3. minor edit to CUDA api : add notion of fat_cubin_handle (currently not used for anything) 4. minor edits to deadlock detection message (more accurate reporting of source of deadlock) 5. other minor edits [git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 7809]
-rw-r--r--.gdbinit2
-rw-r--r--configs/QuadroFX5800/gpgpusim.config5
-rw-r--r--libcuda/cuda_runtime_api.cc8
-rw-r--r--src/cuda-sim/cuda-sim.cc6
-rw-r--r--src/cuda-sim/cuda-sim.h2
-rw-r--r--src/cuda-sim/ptx_loader.cc11
-rw-r--r--src/cuda-sim/ptx_loader.h2
-rw-r--r--src/gpgpu-sim/gpu-cache.cc1
-rw-r--r--src/gpgpu-sim/gpu-cache.h1
-rw-r--r--src/gpgpu-sim/gpu-sim.cc44
-rw-r--r--src/gpgpu-sim/gpu-sim.h3
-rw-r--r--src/gpgpu-sim/l2cache.cc15
-rw-r--r--src/gpgpu-sim/mem_fetch.cc4
-rw-r--r--src/gpgpu-sim/shader.cc24
14 files changed, 72 insertions, 56 deletions
diff --git a/.gdbinit b/.gdbinit
index 6e08528..f803913 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -39,7 +39,7 @@ the next cycle.
end
define dm
- call g_the_gpu.dump_pipeline(0x100,$arg0,0)
+ call g_the_gpu.dump_pipeline(0x10000|0x10000000,0,$arg0)
end
define ptxdis
diff --git a/configs/QuadroFX5800/gpgpusim.config b/configs/QuadroFX5800/gpgpusim.config
index 4ce24a3..8c14ea3 100644
--- a/configs/QuadroFX5800/gpgpusim.config
+++ b/configs/QuadroFX5800/gpgpusim.config
@@ -25,8 +25,9 @@
-gpgpu_const_port_per_bank 2
-gpgpu_interwarp_mshr_merge 6
-gpgpu_cache:dl1 128:64:4:L
--gpgpu_tex_cache:l1 64:64:2:L
--gpgpu_const_cache:l1 64:64:2:L
+-gpgpu_tex_cache:l1 8:32:20:L
+-gpgpu_const_cache:l1 64:64:2:L
+-gpgpu_cache:dl2 64:32:8:L
# interconnection
-network_mode 1
diff --git a/libcuda/cuda_runtime_api.cc b/libcuda/cuda_runtime_api.cc
index adfcb16..c7d19e0 100644
--- a/libcuda/cuda_runtime_api.cc
+++ b/libcuda/cuda_runtime_api.cc
@@ -1036,6 +1036,8 @@ int CUDARTAPI __cudaSynchronizeThreads(void**, void*)
void** CUDARTAPI __cudaRegisterFatBinary( void *fatCubin )
{
+ static unsigned next_fat_bin_handle = 1;
+ unsigned fat_cubin_handle = next_fat_bin_handle++;
#if (CUDART_VERSION >= 2010)
__cudaFatCudaBinary *info = (__cudaFatCudaBinary *)fatCubin;
assert( info->version >= 3 );
@@ -1054,10 +1056,10 @@ void** CUDARTAPI __cudaRegisterFatBinary( void *fatCubin )
if ( selected_capability_offset != (unsigned)-1 ) {
printf("GPGPU-Sim PTX: __cudaRegisterFatBinary found %u PTX versions for '%s', ", num_ptx_versions, info->ident);
printf("selected = %s\n", info->ptx[selected_capability_offset].gpuProfileName );
- gpgpu_ptx_sim_add_ptxstring( info->ptx[selected_capability_offset].ptx, info->cubin[selected_capability_offset].cubin, info->ident );
+ gpgpu_ptx_sim_add_ptxstring( fat_cubin_handle, info->ptx[selected_capability_offset].ptx, info->cubin[selected_capability_offset].cubin, info->ident );
}
#endif
- return 0;
+ return (void**)fat_cubin_handle;
}
void __cudaUnregisterFatBinary(void **fatCubinHandle)
{
@@ -1077,7 +1079,7 @@ void CUDARTAPI __cudaRegisterFunction(
dim3 *gDim
)
{
- gpgpu_ptx_sim_register_kernel(hostFun,deviceFun);
+ gpgpu_ptx_sim_register_kernel(fatCubinHandle,hostFun,deviceFun);
return;
}
diff --git a/src/cuda-sim/cuda-sim.cc b/src/cuda-sim/cuda-sim.cc
index de4445a..afadbe4 100644
--- a/src/cuda-sim/cuda-sim.cc
+++ b/src/cuda-sim/cuda-sim.cc
@@ -1396,7 +1396,7 @@ void print_splash()
}
}
-void gpgpu_ptx_sim_register_kernel(const char *hostFun, const char *deviceFun)
+void gpgpu_ptx_sim_register_kernel(void **fatCubinHandle,const char *hostFun, const char *deviceFun)
{
const void* key=hostFun;
print_splash();
@@ -1428,16 +1428,12 @@ void gpgpu_ptx_sim_register_const_variable(void *hostVar, const char *deviceName
{
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 )
diff --git a/src/cuda-sim/cuda-sim.h b/src/cuda-sim/cuda-sim.h
index 57f2837..fa42341 100644
--- a/src/cuda-sim/cuda-sim.h
+++ b/src/cuda-sim/cuda-sim.h
@@ -41,7 +41,7 @@ extern void gpgpu_ptx_sim_memcpy_gpu_to_gpu( size_t dst, size_t src, size_t co
extern void gpgpu_ptx_sim_memset( size_t dst_start_addr, int c, size_t count );
extern void gpgpu_ptx_sim_init_memory();
extern void gpgpu_ptx_sim_load_gpu_kernels();
-extern void gpgpu_ptx_sim_register_kernel(const char *hostFun, const char *deviceFun);
+extern void gpgpu_ptx_sim_register_kernel(void **fatCubinHandle,const char *hostFun, const char *deviceFun);
extern void gpgpu_ptx_sim_register_const_variable(void*, const char *deviceName, size_t size );
extern void gpgpu_ptx_sim_register_global_variable(void *hostVar, const char *deviceName, size_t size );
extern void gpgpu_ptx_sim_memcpy_symbol(const char *hostVar, const void *src, size_t count, size_t offset, int to );
diff --git a/src/cuda-sim/ptx_loader.cc b/src/cuda-sim/ptx_loader.cc
index 1aa5101..cea9912 100644
--- a/src/cuda-sim/ptx_loader.cc
+++ b/src/cuda-sim/ptx_loader.cc
@@ -78,6 +78,7 @@ memory_space *g_param_mem;
bool g_override_embedded_ptx = false;
struct ptx_info_t {
+ unsigned fat_cubin_handle;
char *str;
char *cubin_str;
char *fname;
@@ -292,15 +293,21 @@ void gpgpu_ptx_sim_load_gpu_kernels()
}
}
-void gpgpu_ptx_sim_add_ptxstring( const char *ptx_string, const char *cubin_string, const char *sourcefname )
+void gpgpu_ptx_sim_add_ptxstring( unsigned fat_cubin_handle, const char *ptx_string, const char *cubin_string, const char *sourcefname )
{
ptx_info_t *t = new ptx_info_t;
t->next = NULL;
t->str = strdup(ptx_string);
+ t->fat_cubin_handle = fat_cubin_handle;
if (cubin_string != NULL) {
t->cubin_str = strdup(cubin_string);
} else {
- assert(g_ptx_convert_to_ptxplus == 0);
+ if(g_ptx_convert_to_ptxplus != 0) {
+ printf("GPGPU-Sim PTX: ERROR cubin information, required for ptxplus, missing from fat bin object\n");
+ printf("GPGPU-Sim PTX: ptxplus currently requires CUDA 2.3 or earlier... disable ptxplus by removing\n");
+ printf("GPGPU-Sim PTX: \'-gpgpu_ptx_convert_to_ptxplus 1\' in \'gpgpusim.config\'\n");
+ abort();
+ }
t->cubin_str = NULL;
}
t->fname = strdup(sourcefname);
diff --git a/src/cuda-sim/ptx_loader.h b/src/cuda-sim/ptx_loader.h
index 01dc0b7..432ee10 100644
--- a/src/cuda-sim/ptx_loader.h
+++ b/src/cuda-sim/ptx_loader.h
@@ -75,7 +75,7 @@ extern memory_space *g_param_mem;
extern bool g_override_embedded_ptx;
void gpgpu_ptx_sim_load_gpu_kernels();
-void gpgpu_ptx_sim_add_ptxstring( const char *ptx_string, const char *cubin_string, const char *sourcefname );
+void gpgpu_ptx_sim_add_ptxstring( unsigned cubin_handle, const char *ptx_string, const char *cubin_string, const char *sourcefname );
class symbol_table *gpgpu_ptx_sim_load_ptx_from_string( const char *p, const char *p_for_info, unsigned source_num );
char* gpgpu_ptx_sim_convert_ptx_to_ptxplus(const char *ptx_str, const char *cubin_str, unsigned source_num);
diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc
index 164c3fd..6f2bbca 100644
--- a/src/gpgpu-sim/gpu-cache.cc
+++ b/src/gpgpu-sim/gpu-cache.cc
@@ -131,7 +131,6 @@ cache_t::cache_t( const char *name,
}
enum cache_request_status cache_t::access( unsigned long long int addr,
- unsigned int nbytes,
unsigned char write,
unsigned int sim_cycle,
address_type *wb_address )
diff --git a/src/gpgpu-sim/gpu-cache.h b/src/gpgpu-sim/gpu-cache.h
index 6efb42d..617307c 100644
--- a/src/gpgpu-sim/gpu-cache.h
+++ b/src/gpgpu-sim/gpu-cache.h
@@ -116,7 +116,6 @@ public:
~cache_t();
enum cache_request_status access( new_addr_type addr,
- unsigned int nbytes,
unsigned char write,
unsigned int sim_cycle,
address_type *wb_address);
diff --git a/src/gpgpu-sim/gpu-sim.cc b/src/gpgpu-sim/gpu-sim.cc
index 23dc651..563dbae 100644
--- a/src/gpgpu-sim/gpu-sim.cc
+++ b/src/gpgpu-sim/gpu-sim.cc
@@ -729,7 +729,7 @@ unsigned int gpgpu_sim::run_gpu_sim()
last_gpu_sim_insn = 0;
while (not_completed || mem_busy || icnt2mem_busy) {
- gpu_sim_loop();
+ cycle();
not_completed = 0;
for (unsigned i=0;i<m_n_shader;i++)
not_completed += m_sc[i]->get_not_completed();
@@ -771,7 +771,8 @@ unsigned int gpgpu_sim::run_gpu_sim()
if (gpu_deadlock_detect && gpu_deadlock) {
fflush(stdout);
- printf("GPGPU-Sim uArch: ERROR ** deadlock detected: last writeback @ gpu_sim_cycle %u (+ gpu_tot_sim_cycle %u) (%u cycles ago)\n",
+ printf("GPGPU-Sim uArch: ERROR ** deadlock detected: last writeback core %u @ gpu_sim_cycle %u (+ gpu_tot_sim_cycle %u) (%u cycles ago)\n",
+ gpu_sim_insn_last_update_sid,
(unsigned) gpu_sim_insn_last_update, (unsigned) (gpu_tot_sim_cycle-gpu_sim_cycle),
(unsigned) (gpu_sim_cycle - gpu_sim_insn_last_update ));
unsigned num_cores=0;
@@ -791,9 +792,9 @@ unsigned int gpgpu_sim::run_gpu_sim()
}
printf("\n");
for (unsigned i=0;i<m_n_mem;i++) {
- mem_busy += m_memory_partition_unit[i]->busy();
- if( mem_busy )
- printf("GPGPU-Sim uArch DEADLOCK: memory partition %u still busy\n", i);
+ bool busy = m_memory_partition_unit[i]->busy();
+ if( busy )
+ printf("GPGPU-Sim uArch DEADLOCK: memory partition %u busy\n", i );
}
if( icnt_busy() )
printf("GPGPU-Sim uArch DEADLOCK: iterconnect contains traffic\n");
@@ -1166,9 +1167,7 @@ void shader_core_ctx::issue_block2core( kernel_info_t &kernel )
}
-
///////////////////////////////////////////////////////////////////////////////////////////
-// wrapper code to to create an illusion of a memory controller with L2 cache.
void memory_partition_unit::push( mem_fetch* req, unsigned long long cycle )
{
@@ -1200,15 +1199,17 @@ void memory_partition_unit::push( mem_fetch* req, unsigned long long cycle )
mem_fetch* memory_partition_unit::pop()
{
mem_fetch* mf;
- if (m_config->gpgpu_cache_dl2_opt) {
+ if( m_config->gpgpu_cache_dl2_opt ) {
mf = L2tocbqueue->pop(gpu_sim_cycle);
- if ( mf->isatomic() )
+ if( mf && mf->isatomic() )
mf->do_atomic();
} else {
mf = m_dram->returnq_pop(gpu_sim_cycle);
- if (mf) mf->set_type( REPLY_DATA );
- if (mf->isatomic() )
- mf->do_atomic();
+ if( mf ) {
+ mf->set_type( REPLY_DATA );
+ if( mf->isatomic() )
+ mf->do_atomic();
+ }
}
m_request_tracker.erase(mf);
return mf;
@@ -1232,11 +1233,16 @@ void memory_partition_unit::issueCMD()
if ( !(dramtoL2queue->full() || dramtoL2writequeue->full()) ) {
mem_fetch* mf = m_dram->pop();
if (mf) {
- if (m_config->gpgpu_l2_readoverwrite && mf->get_is_write() )
- dramtoL2writequeue->push(mf,gpu_sim_cycle);
- else
- dramtoL2queue->push(mf,gpu_sim_cycle);
- mf->set_status(IN_DRAMTOL2QUEUE,MR_DRAM_OUTQ,gpu_sim_cycle+gpu_tot_sim_cycle);
+ if( mf->get_mem_acc() == L2_WRBK_ACC ) {
+ m_request_tracker.erase(mf);
+ delete mf;
+ } else {
+ if (m_config->gpgpu_l2_readoverwrite && mf->get_is_write() )
+ dramtoL2writequeue->push(mf,gpu_sim_cycle);
+ else
+ dramtoL2queue->push(mf,gpu_sim_cycle);
+ mf->set_status(IN_DRAMTOL2QUEUE,MR_DRAM_OUTQ,gpu_sim_cycle+gpu_tot_sim_cycle);
+ }
}
}
} else {
@@ -1289,7 +1295,7 @@ int gpgpu_sim::next_clock_domain(void)
unsigned long long g_single_step=0; // set this in gdb to single step the pipeline
-void gpgpu_sim::gpu_sim_loop()
+void gpgpu_sim::cycle()
{
int clock_mask = next_clock_domain();
@@ -1317,6 +1323,8 @@ void gpgpu_sim::gpu_sim_loop()
} else {
gpu_stall_icnt2sh++;
}
+ } else {
+ m_memory_partition_unit[i]->pop();
}
}
}
diff --git a/src/gpgpu-sim/gpu-sim.h b/src/gpgpu-sim/gpu-sim.h
index 2aad778..48a4c52 100644
--- a/src/gpgpu-sim/gpu-sim.h
+++ b/src/gpgpu-sim/gpu-sim.h
@@ -297,7 +297,7 @@ private:
int next_clock_domain(void);
unsigned char check_icnt_has_buffer(unsigned long long int addr, int bsize, int sid );
- void gpu_sim_loop();
+ void cycle();
void fq_pop(int tpc_id);
void L2c_options(class OptionParser *opp);
void L2c_print_cache_stat() const;
@@ -352,6 +352,7 @@ private:
class memory_stats_t *m_memory_stats;
public:
unsigned long long gpu_sim_insn_last_update;
+ unsigned gpu_sim_insn_last_update_sid;
};
// global counters
diff --git a/src/gpgpu-sim/l2cache.cc b/src/gpgpu-sim/l2cache.cc
index e315b45..411f452 100644
--- a/src/gpgpu-sim/l2cache.cc
+++ b/src/gpgpu-sim/l2cache.cc
@@ -399,9 +399,12 @@ void memory_partition_unit::L2c_service_mem_req()
case RD_REQ:
case WT_REQ: {
address_type rep_block;
- enum cache_request_status status = m_L2cache->access( mf->get_addr(), 4, mf->get_is_write(), gpu_sim_cycle, &rep_block);
+ enum cache_request_status status = MISS_NO_WB;
+ if( mf->istexture() )
+ status = m_L2cache->access( mf->get_addr(), mf->get_is_write(), gpu_sim_cycle, &rep_block);
if( (status==HIT) || m_config->l2_ideal ) {
mf->set_type( REPLY_DATA );
+ assert( mf != NULL );
L2tocbqueue->push(mf,gpu_sim_cycle);
if (!mf->get_is_write()) {
m_stats->L2_read_hit++;
@@ -417,9 +420,8 @@ void memory_partition_unit::L2c_service_mem_req()
// if a miss hits in the mshr, that means there is another inflight request for the same data
// this miss just need to access the cache later when this request is serviced
bool mshr_hit = m_mshr->new_miss(mf);
- if (not mshr_hit) {
+ if (not mshr_hit)
L2todramqueue->push(mf,gpu_sim_cycle);
- }
mf->set_status(IN_L2TODRAMQUEUE,MR_DRAM_OUTQ,gpu_sim_cycle+gpu_tot_sim_cycle);
}
}
@@ -481,7 +483,8 @@ void memory_partition_unit::process_dram_output()
//only transfer across icnt once the whole line has been received by L2 cache
mf->set_type(REPLY_DATA);
L2tocbqueue->push(mf,gpu_sim_cycle);
- wb_addr = m_L2cache->shd_cache_fill(mf->get_addr(), gpu_sim_cycle);
+ if( mf->istexture() )
+ wb_addr = m_L2cache->shd_cache_fill(mf->get_addr(), gpu_sim_cycle);
}
// only perform a write on cache eviction (write-back policy)
// it is the 1st or nth time trial to writeback
@@ -499,8 +502,8 @@ void memory_partition_unit::process_dram_output()
} else { //service L2 write miss
m_missTracker->miss_serviced(mf);
freed_L2write_mfs++;
- m_request_tracker.erase(mf);
- delete mf;
+ mf->set_type(REPLY_DATA);
+ L2tocbqueue->push(mf,gpu_sim_cycle);
gpgpu_n_processed_writes++;
L2dramout = NULL;
wb_addr = -1;
diff --git a/src/gpgpu-sim/mem_fetch.cc b/src/gpgpu-sim/mem_fetch.cc
index a3a423a..296a834 100644
--- a/src/gpgpu-sim/mem_fetch.cc
+++ b/src/gpgpu-sim/mem_fetch.cc
@@ -105,8 +105,10 @@ mem_fetch::mem_fetch( new_addr_type addr,
void mem_fetch::print( FILE *fp ) const
{
- fprintf(fp," mf: uid=%6u, addr=0x%08llx, sid=%u, wid=%u, pc=0x%04x, %s, bank=%u\n",
+ fprintf(fp," mf: uid=%6u, addr=0x%08llx, sid=%u, wid=%u, pc=0x%04x, %s, bank=%u, ",
request_uid, addr, sid, wid, pc, (m_write?"write":"read "), tlx.bk);
+ if( mshr ) mshr->print(fp,0x100);
+ else fprintf(fp,"\n");
}
void mem_fetch::set_status( enum mshr_status status, enum mem_req_stat stat, unsigned long long cycle )
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index c470c94..495c4c0 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -1160,7 +1160,7 @@ void shader_core_ctx::fetch_new()
unsigned offset_in_block = pc & (m_L1I->get_line_sz()-1);
if( (offset_in_block+nbytes) > m_L1I->get_line_sz() )
nbytes = (m_L1I->get_line_sz()-offset_in_block);
- enum cache_request_status status = m_L1I->access( (unsigned long long)pc, nbytes, 0, gpu_sim_cycle, &wb );
+ enum cache_request_status status = m_L1I->access( (unsigned long long)pc, 0, gpu_sim_cycle, &wb );
if( status != HIT ) {
unsigned req_size = READ_PACKET_SIZE;
if( m_gpu->fq_has_buffer(ppc, req_size, false, m_sid) ) {
@@ -1851,10 +1851,9 @@ void shader_core_ctx::memory_const_process_warp()
accessq[i].cache_hit = true;
} else {
cache_request_status status = m_L1C->access( accessq[i].addr,
- WORD_SIZE, //this field is ingored.
- 0, //should always be a read
- gpu_sim_cycle+gpu_tot_sim_cycle,
- NULL/*should never writeback*/);
+ 0, //should always be a read
+ gpu_sim_cycle+gpu_tot_sim_cycle,
+ NULL/*should never writeback*/);
accessq[i].cache_hit = (status == HIT);
if (m_config->gpgpu_perfect_mem) accessq[i].cache_hit = true;
if (accessq[i].cache_hit) m_stats->L1_const_miss++;
@@ -1878,10 +1877,9 @@ void shader_core_ctx::memory_texture_process_warp()
//do cache checks here for each request (non-hardware), could be done later for more accurate timing of cache accesses, but probably uneccesary;
for (unsigned i = qbegin; i < accessq.size(); i++) {
cache_request_status status = m_L1T->access( accessq[i].addr,
- WORD_SIZE, //this field is ignored.
- 0, //should always be a read
- gpu_sim_cycle+gpu_tot_sim_cycle,
- NULL /*should never writeback*/);
+ 0, //should always be a read
+ gpu_sim_cycle+gpu_tot_sim_cycle,
+ NULL /*should never writeback*/);
accessq[i].cache_hit = (status == HIT);
if (m_config->gpgpu_perfect_mem) accessq[i].cache_hit = true;
if (accessq[i].cache_hit) m_stats->L1_texture_miss++;
@@ -2150,10 +2148,9 @@ mem_stage_stall_type shader_core_ctx::dcache_check(mem_access_t& access)
if (!m_config->gpgpu_no_dl1 && !m_config->gpgpu_perfect_mem) {
//check cache
cache_request_status status = m_L1D->access( access.addr,
- WORD_SIZE, //this field is ignored.
- access.iswrite,
- gpu_sim_cycle+gpu_tot_sim_cycle,
- &access.wb_addr );
+ access.iswrite,
+ gpu_sim_cycle+gpu_tot_sim_cycle,
+ &access.wb_addr );
if (status == RESERVATION_FAIL) {
access.cache_checked = false;
return WB_CACHE_RSRV_FAIL;
@@ -2370,6 +2367,7 @@ void shader_core_ctx::writeback()
if ( !is_const(done_inst.space) )
m_stats->gpu_sim_insn_no_ld_const++;
m_gpu->gpu_sim_insn_last_update = gpu_sim_cycle;
+ m_gpu->gpu_sim_insn_last_update_sid = m_sid;
m_num_sim_insn++;
m_thread[done_inst.hw_thread_id].n_insn++;
m_thread[done_inst.hw_thread_id].n_insn_ac++;