// File: CMU-SAFARI[P]MQSim/src/ssd/GC_and_WL_Unit_Base.cpp
// Function: SSD_Components::GC_and_WL_Unit_Base::run_static_wearleveling(NVM::FlashMemory::Physical_Page_Address)
void GC_and_WL_Unit_Base::run_static_wearleveling(const NVM::FlashMemory::Physical_Page_Address plane_address)
	{
		PlaneBookKeepingType* pbke = block_manager->Get_plane_bookkeeping_entry(plane_address);
		flash_block_ID_type wl_candidate_block_id = block_manager->Get_coldest_block_id(plane_address);
		if (!is_safe_gc_wl_candidate(pbke, wl_candidate_block_id)) {
			return;
		}

		NVM::FlashMemory::Physical_Page_Address wl_candidate_address(plane_address);
		wl_candidate_address.BlockID = wl_candidate_block_id;
		Block_Pool_Slot_Type* block = &pbke->Blocks[wl_candidate_block_id];

		//Run the state machine to protect against race condition
		block_manager->GC_WL_started(wl_candidate_block_id);
		pbke->Ongoing_erase_operations.insert(wl_candidate_block_id);
		address_mapping_unit->Set_barrier_for_accessing_physical_block(wl_candidate_address);//Lock the block, so no user request can intervene while the GC is progressing
		if (block_manager->Can_execute_gc_wl(wl_candidate_address)) {//If there are ongoing requests targeting the candidate block, the gc execution should be postponed
			Stats::Total_wl_executions++;
			tsu->Prepare_for_transaction_submit();

			NVM_Transaction_Flash_ER* wl_erase_tr = new NVM_Transaction_Flash_ER(Transaction_Source_Type::GC_WL, pbke->Blocks[wl_candidate_block_id].Stream_id, wl_candidate_address);
			if (block->Current_page_write_index - block->Invalid_page_count > 0) {//If there are some valid pages in block, then prepare flash transactions for page movement
				NVM_Transaction_Flash_RD* wl_read = NULL;
				NVM_Transaction_Flash_WR* wl_write = NULL;
				for (flash_page_ID_type pageID = 0; pageID < block->Current_page_write_index; pageID++) {
					if (block_manager->Is_page_valid(block, pageID)) {
						Stats::Total_page_movements_for_gc;
						wl_candidate_address.PageID = pageID;
						if (use_copyback) {
							wl_write = new NVM_Transaction_Flash_WR(Transaction_Source_Type::GC_WL, block->Stream_id, sector_no_per_page * SECTOR_SIZE_IN_BYTE,
								NO_LPA, address_mapping_unit->Convert_address_to_ppa(wl_candidate_address), NULL, 0, NULL, 0, INVALID_TIME_STAMP);
							wl_write->ExecutionMode = WriteExecutionModeType::COPYBACK;
							tsu->Submit_transaction(wl_write);
						} else {
							wl_read = new NVM_Transaction_Flash_RD(Transaction_Source_Type::GC_WL, block->Stream_id, sector_no_per_page * SECTOR_SIZE_IN_BYTE,
								NO_LPA, address_mapping_unit->Convert_address_to_ppa(wl_candidate_address), wl_candidate_address, NULL, 0, NULL, 0, INVALID_TIME_STAMP);
							wl_write = new NVM_Transaction_Flash_WR(Transaction_Source_Type::GC_WL, block->Stream_id, sector_no_per_page * SECTOR_SIZE_IN_BYTE,
								NO_LPA, NO_PPA, wl_candidate_address, NULL, 0, wl_read, 0, INVALID_TIME_STAMP);
							wl_write->ExecutionMode = WriteExecutionModeType::SIMPLE;
							wl_write->RelatedErase = wl_erase_tr;
							wl_read->RelatedWrite = wl_write;
							tsu->Submit_transaction(wl_read);//Only the read transaction would be submitted. The Write transaction is submitted when the read transaction is finished and the LPA of the target page is determined
						}
						wl_erase_tr->Page_movement_activities.push_back(wl_write);
					}
				}
			}
			block->Erase_transaction = wl_erase_tr;
			tsu->Submit_transaction(wl_erase_tr);

			tsu->Schedule();
		}
	}

// Assembly:
subq $0x128, %rsp            # imm = 0x128
movq %rsi, 0x80(%rsp)
movq %rdi, 0x120(%rsp)
movq %rsi, 0x118(%rsp)
movq 0x120(%rsp), %rax
movq %rax, 0x88(%rsp)
movq 0x38(%rax), %rdi
callq 0x7cb40
movq 0x80(%rsp), %rsi
movq %rax, %rcx
movq 0x88(%rsp), %rax
movq %rcx, 0x110(%rsp)
movq 0x38(%rax), %rdi
callq 0x7ca80
movq 0x88(%rsp), %rdi
movl %eax, 0x10c(%rsp)
movq 0x110(%rsp), %rsi
movl 0x10c(%rsp), %edx
callq 0x81b80
testb $0x1, %al
jne 0x81478
jmp 0x81a19
movq 0x80(%rsp), %rsi
leaq 0xf4(%rsp), %rdi
callq 0x5e380
movq 0x88(%rsp), %rax
movl 0x10c(%rsp), %ecx
movl %ecx, 0x104(%rsp)
movq 0x110(%rsp), %rcx
movq 0x10(%rcx), %rcx
movl 0x10c(%rsp), %edx
shlq $0x6, %rdx
addq %rdx, %rcx
movq %rcx, 0xe8(%rsp)
movq 0x38(%rax), %rax
movq %rax, 0x78(%rsp)
movl 0x10c(%rsp), %esi
leaq 0xd0(%rsp), %rdi
xorl %r9d, %r9d
movl %r9d, %edx
movl %r9d, %ecx
movl %r9d, %r8d
movl $0x0, (%rsp)
callq 0x4a220
movq 0x78(%rsp), %rdi
leaq 0xd0(%rsp), %rsi
callq 0x7cc90
movq 0x110(%rsp), %rdi
addq $0xb0, %rdi
leaq 0x10c(%rsp), %rsi
callq 0x5e630
movq %rax, %rcx
movq 0x88(%rsp), %rax
movq %rcx, 0xc0(%rsp)
movb %dl, 0xc8(%rsp)
movq 0x30(%rax), %rdi
movq (%rdi), %rax
leaq 0xf4(%rsp), %rsi
callq *0x98(%rax)
movq 0x88(%rsp), %rax
movq 0x38(%rax), %rdi
leaq 0xf4(%rsp), %rsi
callq 0x7cc00
testb $0x1, %al
jne 0x81574
jmp 0x81a19
movq 0x88(%rsp), %rax
leaq 0x5aaad(%rip), %rcx     # 0xdc030
movl (%rcx), %edx
incl %edx
movl %edx, (%rcx)
movq 0x40(%rax), %rdi
callq 0x59940
movl $0x98, %edi
callq 0x8450
movq %rax, %rdi
movq %rdi, %rax
movq %rax, 0x70(%rsp)
movq 0x110(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x10c(%rsp), %ecx
shlq $0x6, %rcx
movzwl 0x20(%rax,%rcx), %edx
movl $0x2, %esi
leaq 0xf4(%rsp), %rcx
callq 0x95e40
jmp 0x815d7
movq 0x70(%rsp), %rax
movq %rax, 0xb8(%rsp)
movq 0xe8(%rsp), %rax
movl 0x4(%rax), %eax
movq 0xe8(%rsp), %rcx
subl 0xc(%rcx), %eax
cmpl $0x0, %eax
jbe 0x819da
movq $0x0, 0xa0(%rsp)
movq $0x0, 0x98(%rsp)
movl $0x0, 0x94(%rsp)
movl 0x94(%rsp), %eax
movq 0xe8(%rsp), %rcx
cmpl 0x4(%rcx), %eax
jae 0x819d8
movq 0x88(%rsp), %rax
movq 0x38(%rax), %rdi
movq 0xe8(%rsp), %rsi
movl 0x94(%rsp), %edx
callq 0x7cfa0
testb $0x1, %al
jne 0x81667
jmp 0x819c0
movq 0x88(%rsp), %rax
movl 0x94(%rsp), %ecx
movl %ecx, 0x108(%rsp)
testb $0x1, 0x64(%rax)
je 0x817b4
movl $0xb0, %edi
callq 0x8450
movq %rax, %rcx
movq 0x88(%rsp), %rax
movq %rcx, 0x58(%rsp)
movq 0xe8(%rsp), %rcx
movw 0x20(%rcx), %cx
movw %cx, 0x62(%rsp)
movl 0x10c(%rax), %ecx
shll $0x9, %ecx
movl %ecx, 0x64(%rsp)
movq 0x30(%rax), %rdi
movq (%rdi), %rax
movq 0x90(%rax), %rax
leaq 0xf4(%rsp), %rsi
callq *%rax
movq %rax, 0x68(%rsp)
jmp 0x816de
movq 0x68(%rsp), %r9
movl 0x64(%rsp), %ecx
movq 0x58(%rsp), %rdi
movw 0x62(%rsp), %ax
movq %rsp, %rdx
movq $-0x1, 0x20(%rdx)
movq $0x0, 0x18(%rdx)
movq $0x0, 0x10(%rdx)
movq $0x0, 0x8(%rdx)
movq $0x0, (%rdx)
movzwl %ax, %edx
movl $0x2, %esi
movq $-0x1, %r8
callq 0x96320
jmp 0x81731
movq 0x88(%rsp), %rax
movq 0x58(%rsp), %rcx
movq %rcx, 0x98(%rsp)
movq 0x98(%rsp), %rcx
movl $0x1, 0xa8(%rcx)
movq 0x40(%rax), %rdi
movq 0x98(%rsp), %rsi
callq 0x59980
jmp 0x819a4
movq 0x70(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xb0(%rsp)
movl %eax, 0xac(%rsp)
callq 0x8410
jmp 0x81a21
movq 0x58(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xb0(%rsp)
movl %eax, 0xac(%rsp)
callq 0x8410
jmp 0x81a21
movl $0xa0, %edi
callq 0x8450
movq %rax, %rcx
movq 0x88(%rsp), %rax
movq %rcx, 0x40(%rsp)
movq 0xe8(%rsp), %rcx
movw 0x20(%rcx), %cx
movw %cx, 0x4a(%rsp)
movl 0x10c(%rax), %ecx
shll $0x9, %ecx
movl %ecx, 0x4c(%rsp)
movq 0x30(%rax), %rdi
movq (%rdi), %rax
movq 0x90(%rax), %rax
leaq 0xf4(%rsp), %rsi
callq *%rax
movq %rax, 0x50(%rsp)
jmp 0x8180b
movq 0x50(%rsp), %r9
movl 0x4c(%rsp), %ecx
movq 0x40(%rsp), %rdi
movw 0x4a(%rsp), %ax
movq %rsp, %rdx
leaq 0xf4(%rsp), %rsi
movq %rsi, (%rdx)
movq $-0x1, 0x28(%rdx)
movq $0x0, 0x20(%rdx)
movq $0x0, 0x18(%rdx)
movq $0x0, 0x10(%rdx)
movq $0x0, 0x8(%rdx)
movzwl %ax, %edx
movl $0x2, %esi
movq $-0x1, %r8
callq 0x95fe0
jmp 0x8186a
movq 0x40(%rsp), %rax
movq %rax, 0xa0(%rsp)
movl $0xb0, %edi
callq 0x8450
movq %rax, %rdi
movq 0x88(%rsp), %rax
movq %rdi, %rcx
movq %rcx, 0x38(%rsp)
movq 0xe8(%rsp), %rcx
movzwl 0x20(%rcx), %edx
movl 0x10c(%rax), %ecx
shll $0x9, %ecx
movq 0xa0(%rsp), %rsi
movq %rsp, %rax
movq %rsi, 0x18(%rax)
leaq 0xf4(%rsp), %rsi
movq %rsi, (%rax)
movq $-0x1, 0x28(%rax)
movq $0x0, 0x20(%rax)
movq $0x0, 0x10(%rax)
movq $0x0, 0x8(%rax)
movl $0x2, %esi
movq $-0x1, %r9
movq %r9, %r8
callq 0x96240
jmp 0x818f9
movq 0x88(%rsp), %rax
movq 0x38(%rsp), %rcx
movq %rcx, 0x98(%rsp)
movq 0x98(%rsp), %rcx
movl $0x0, 0xa8(%rcx)
movq 0xb8(%rsp), %rdx
movq 0x98(%rsp), %rcx
movq %rdx, 0x90(%rcx)
movq 0x98(%rsp), %rdx
movq 0xa0(%rsp), %rcx
movq %rdx, 0x88(%rcx)
movq 0x40(%rax), %rdi
movq 0xa0(%rsp), %rsi
callq 0x59980
jmp 0x819a4
movq 0x40(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xb0(%rsp)
movl %eax, 0xac(%rsp)
callq 0x8410
jmp 0x81a21
movq 0x38(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xb0(%rsp)
movl %eax, 0xac(%rsp)
callq 0x8410
jmp 0x81a21
movq 0xb8(%rsp), %rdi
addq $0x80, %rdi
leaq 0x98(%rsp), %rsi
callq 0x81ce0
jmp 0x819c2
movl 0x94(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x94(%rsp)
jmp 0x81626
jmp 0x819da
movq 0x88(%rsp), %rax
movq 0xb8(%rsp), %rdx
movq 0xe8(%rsp), %rcx
movq %rdx, 0x28(%rcx)
movq 0x40(%rax), %rdi
movq 0xb8(%rsp), %rsi
callq 0x59980
movq 0x88(%rsp), %rax
movq 0x40(%rax), %rdi
movq (%rdi), %rax
callq *0x30(%rax)
addq $0x128, %rsp            # imm = 0x128
retq
movq 0xb0(%rsp), %rdi
callq 0x8770
nop