// File: /workspace/github_cpp_makefile_star2_O2/rossy[P]crossc/SPIRV-Cross/spirv_cross.cpp
// Function: spirv_cross::Compiler::analyze_variable_scope(spirv_cross::SPIRFunction&, spirv_cross::Compiler::AnalyzeVariableScopeAccessHandler&)
void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeAccessHandler &handler)
{
	// First, we map out all variable access within a function.
	// Essentially a map of block -> { variables accessed in the basic block }
	traverse_all_reachable_opcodes(entry, handler);

	auto &cfg = *function_cfgs.find(entry.self)->second;

	// Analyze if there are parameters which need to be implicitly preserved with an "in" qualifier.
	analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block,
	                               handler.complete_write_variables_to_block);

	unordered_map<uint32_t, uint32_t> potential_loop_variables;

	// For each variable which is statically accessed.
	for (auto &var : handler.accessed_variables_to_block)
	{
		// Only deal with variables which are considered local variables in this function.
		if (find(begin(entry.local_variables), end(entry.local_variables), var.first) == end(entry.local_variables))
			continue;

		DominatorBuilder builder(cfg);
		auto &blocks = var.second;
		auto &type = expression_type(var.first);

		// Figure out which block is dominating all accesses of those variables.
		for (auto &block : blocks)
		{
			// If we're accessing a variable inside a continue block, this variable might be a loop variable.
			// We can only use loop variables with scalars, as we cannot track static expressions for vectors.
			if (is_continue(block))
			{
				// Potentially awkward case to check for.
				// We might have a variable inside a loop, which is touched by the continue block,
				// but is not actually a loop variable.
				// The continue block is dominated by the inner part of the loop, which does not make sense in high-level
				// language output because it will be declared before the body,
				// so we will have to lift the dominator up to the relevant loop header instead.
				builder.add_block(ir.continue_block_to_loop_header[block]);

				// Arrays or structs cannot be loop variables.
				if (type.vecsize == 1 && type.columns == 1 && type.basetype != SPIRType::Struct && type.array.empty())
				{
					// The variable is used in multiple continue blocks, this is not a loop
					// candidate, signal that by setting block to -1u.
					auto &potential = potential_loop_variables[var.first];

					if (potential == 0)
						potential = block;
					else
						potential = ~(0u);
				}
			}
			builder.add_block(block);
		}

		builder.lift_continue_block_dominator();

		// Add it to a per-block list of variables.
		uint32_t dominating_block = builder.get_dominator();

		// If all blocks here are dead code, this will be 0, so the variable in question
		// will be completely eliminated.
		if (dominating_block)
		{
			auto &block = get<SPIRBlock>(dominating_block);
			block.dominated_variables.push_back(var.first);
			get<SPIRVariable>(var.first).dominator = dominating_block;
		}
	}

	for (auto &var : handler.accessed_temporaries_to_block)
	{
		auto itr = handler.result_id_to_type.find(var.first);

		if (itr == end(handler.result_id_to_type))
		{
			// We found a false positive ID being used, ignore.
			// This should probably be an assert.
			continue;
		}

		DominatorBuilder builder(cfg);
		bool force_temporary = false;

		// Figure out which block is dominating all accesses of those temporaries.
		auto &blocks = var.second;
		for (auto &block : blocks)
		{
			builder.add_block(block);

			// If a temporary is used in more than one block, we might have to lift continue block
			// access up to loop header like we did for variables.
			if (blocks.size() != 1 && is_continue(block))
				builder.add_block(ir.continue_block_to_loop_header[block]);
			else if (blocks.size() != 1 && is_single_block_loop(block))
			{
				// Awkward case, because the loop header is also the continue block.
				force_temporary = true;
			}
		}

		uint32_t dominating_block = builder.get_dominator();
		if (dominating_block)
		{
			// If we touch a variable in the dominating block, this is the expected setup.
			// SPIR-V normally mandates this, but we have extra cases for temporary use inside loops.
			bool first_use_is_dominator = blocks.count(dominating_block) != 0;

			if (!first_use_is_dominator || force_temporary)
			{
				// This should be very rare, but if we try to declare a temporary inside a loop,
				// and that temporary is used outside the loop as well (spirv-opt inliner likes this)
				// we should actually emit the temporary outside the loop.
				hoisted_temporaries.insert(var.first);
				forced_temporaries.insert(var.first);

				auto &block_temporaries = get<SPIRBlock>(dominating_block).declare_temporary;
				block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
			}
			else if (blocks.size() > 1)
			{
				// Keep track of the temporary as we might have to declare this temporary.
				// This can happen if the loop header dominates a temporary, but we have a complex fallback loop.
				// In this case, the header is actually inside the for (;;) {} block, and we have problems.
				// What we need to do is hoist the temporaries outside the for (;;) {} block in case the header block
				// declares the temporary.
				auto &block_temporaries = get<SPIRBlock>(dominating_block).potential_declare_temporary;
				block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
			}
		}
	}

	unordered_set<uint32_t> seen_blocks;

	// Now, try to analyze whether or not these variables are actually loop variables.
	for (auto &loop_variable : potential_loop_variables)
	{
		auto &var = get<SPIRVariable>(loop_variable.first);
		auto dominator = var.dominator;
		auto block = loop_variable.second;

		// The variable was accessed in multiple continue blocks, ignore.
		if (block == ~(0u) || block == 0)
			continue;

		// Dead code.
		if (dominator == 0)
			continue;

		uint32_t header = 0;

		// Find the loop header for this block if we are a continue block.
		{
			auto itr = ir.continue_block_to_loop_header.find(block);
			if (itr != end(ir.continue_block_to_loop_header))
			{
				header = itr->second;
			}
			else if (get<SPIRBlock>(block).continue_block == block)
			{
				// Also check for self-referential continue block.
				header = block;
			}
		}

		assert(header);
		auto &header_block = get<SPIRBlock>(header);
		auto &blocks = handler.accessed_variables_to_block[loop_variable.first];

		// If a loop variable is not used before the loop, it's probably not a loop variable.
		bool has_accessed_variable = blocks.count(header) != 0;

		// Now, there are two conditions we need to meet for the variable to be a loop variable.
		// 1. The dominating block must have a branch-free path to the loop header,
		// this way we statically know which expression should be part of the loop variable initializer.

		// Walk from the dominator, if there is one straight edge connecting
		// dominator and loop header, we statically know the loop initializer.
		bool static_loop_init = true;
		while (dominator != header)
		{
			if (blocks.count(dominator) != 0)
				has_accessed_variable = true;

			auto &succ = cfg.get_succeeding_edges(dominator);
			if (succ.size() != 1)
			{
				static_loop_init = false;
				break;
			}

			auto &pred = cfg.get_preceding_edges(succ.front());
			if (pred.size() != 1 || pred.front() != dominator)
			{
				static_loop_init = false;
				break;
			}

			dominator = succ.front();
		}

		if (!static_loop_init || !has_accessed_variable)
			continue;

		// The second condition we need to meet is that no access after the loop
		// merge can occur. Walk the CFG to see if we find anything.

		seen_blocks.clear();
		cfg.walk_from(seen_blocks, header_block.merge_block, [&](uint32_t walk_block) {
			// We found a block which accesses the variable outside the loop.
			if (blocks.find(walk_block) != end(blocks))
				static_loop_init = false;
		});

		if (!static_loop_init)
			continue;

		// We have a loop variable.
		header_block.loop_variables.push_back(loop_variable.first);
		// Need to sort here as variables come from an unordered container, and pushing stuff in wrong order
		// will break reproducability in regression runs.
		sort(begin(header_block.loop_variables), end(header_block.loop_variables));
		get<SPIRVariable>(loop_variable.first).loop_variable = true;
	}
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xc8, %rsp
movq %rdx, %r14
movq %rsi, %r15
movq %rdi, %rbx
callq 0x1157c
leaq 0x3a0(%rbx), %rdi
leaq 0x8(%r15), %rsi
callq 0x1ea20
movq 0x10(%rax), %rdx
leaq 0x18(%r14), %rcx
leaq 0xc0(%r14), %r8
movq %rbx, %rdi
movq %r15, %rsi
movq %rdx, 0x18(%rsp)
movq %rcx, 0x50(%rsp)
callq 0x1499a
leaq 0xc0(%rsp), %rax
movq %rax, -0x30(%rax)
movq $0x1, -0x28(%rax)
xorps %xmm0, %xmm0
movups %xmm0, -0x20(%rax)
movl $0x3f800000, -0x10(%rax) # imm = 0x3F800000
movups %xmm0, -0x8(%rax)
movq %r14, 0x10(%rsp)
leaq 0x28(%r14), %r12
leaq 0x98(%rbx), %rax
movq %rax, 0x30(%rsp)
leaq 0x58(%rsp), %rbp
movq %r15, 0x28(%rsp)
movq (%r12), %r12
testq %r12, %r12
je 0x156be
leaq 0x8(%r12), %r14
movq 0x48(%r15), %rdi
movq 0x50(%r15), %rsi
movq %r14, %rdx
callq 0x6c66
cmpq 0x50(%r15), %rax
je 0x1559a
movq %rbp, %rdi
movq 0x18(%rsp), %rsi
callq 0x6a16
movq %r14, 0x20(%rsp)
movl (%r14), %esi
movq %rbx, %rdi
callq 0x104b0
movq %rax, %r15
leaq 0x20(%r12), %r13
movq (%r13), %r13
testq %r13, %r13
je 0x1566d
leaq 0x8(%r13), %r14
movl 0x8(%r13), %eax
movq 0x80(%rbx), %rcx
testb $0x2, (%rcx,%rax)
je 0x1565d
movq 0x30(%rsp), %rdi
movq %r14, %rsi
callq 0x1d40e
movl (%rax), %esi
movq %rbp, %rdi
callq 0x6a1e
cmpl $0x1, 0x14(%r15)
jne 0x1565d
cmpl $0x1, 0x18(%r15)
jne 0x1565d
cmpl $0xc, 0xc(%r15)
je 0x1565d
movq 0x20(%r15), %rax
cmpq 0x28(%r15), %rax
jne 0x1565d
leaq 0x90(%rsp), %rdi
movq 0x20(%rsp), %rsi
callq 0x1d40e
xorl %ecx, %ecx
cmpl (%rax), %ecx
movl $0x0, %ecx
sbbl %ecx, %ecx
orl (%r14), %ecx
movl %ecx, (%rax)
movl (%r14), %esi
movq %rbp, %rdi
callq 0x6a1e
jmp 0x155e7
movq %rbp, %rdi
callq 0x6a48
movl 0x60(%rsp), %r14d
testl %r14d, %r14d
movq 0x28(%rsp), %r15
movq 0x20(%rsp), %r13
je 0x1559a
movq %rbx, %rdi
movl %r14d, %esi
callq 0x6bf0
addq $0xb8, %rax
movq %rax, %rdi
movq %r13, %rsi
callq 0x6c06
movl (%r13), %esi
movq %rbx, %rdi
callq 0x9c0e
movl %r14d, 0x60(%rax)
jmp 0x1559a
movq 0x10(%rsp), %rax
leaq 0x60(%rax), %r13
addq $0x88, %rax
movq %rax, 0x10(%rsp)
leaq 0x298(%rbx), %rax
movq %rax, 0x28(%rsp)
leaq 0x228(%rbx), %rax
movq %rax, 0x48(%rsp)
leaq 0x58(%rsp), %r15
movq (%r13), %r13
testq %r13, %r13
je 0x15839
leaq 0x8(%r13), %rbp
movq 0x10(%rsp), %rdi
movq %rbp, %rsi
callq 0x1d98c
testq %rax, %rax
je 0x156ef
movq %r15, %rdi
movq 0x18(%rsp), %rsi
callq 0x6a16
leaq 0x10(%r13), %rax
movq %rax, 0x20(%rsp)
leaq 0x20(%r13), %r14
xorl %r12d, %r12d
movq (%r14), %r14
testq %r14, %r14
je 0x15782
movl 0x8(%r14), %esi
movq %r15, %rdi
callq 0x6a1e
cmpq $0x1, 0x28(%r13)
je 0x1572f
leaq 0x8(%r14), %rsi
movl (%rsi), %eax
movq 0x80(%rbx), %rcx
testb $0x2, (%rcx,%rax)
je 0x15773
movq 0x30(%rsp), %rdi
callq 0x1d40e
movl (%rax), %esi
movq %r15, %rdi
callq 0x6a1e
jmp 0x1572f
movq %rbx, %rdi
movl %eax, %esi
callq 0x17c20
orb %al, %r12b
jmp 0x1572f
movl 0x60(%rsp), %eax
movl %eax, 0x38(%rsp)
testl %eax, %eax
je 0x156ef
movq 0x20(%rsp), %rdi
leaq 0x38(%rsp), %rsi
callq 0x1aeea
testq %rax, %rax
sete %al
orb %r12b, %al
testb $0x1, %al
je 0x157fb
movq 0x28(%rsp), %rdi
movq %rbp, %rsi
callq 0x1af30
movq 0x48(%rsp), %rdi
movq %rbp, %rsi
callq 0x1af30
movl 0x38(%rsp), %esi
movq %rbx, %rdi
callq 0x6bf0
movq %rax, %r14
movq 0x10(%rsp), %rdi
movq %rbp, %rsi
callq 0x1d40e
addq $0x68, %r14
movq %r14, %rdi
movq %rax, %rsi
movq %rbp, %rdx
callq 0x17c38
jmp 0x156ef
cmpq $0x2, 0x28(%r13)
jb 0x156ef
movl 0x38(%rsp), %esi
movq %rbx, %rdi
callq 0x6bf0
movq %rax, %r14
movq 0x10(%rsp), %rdi
movq %rbp, %rsi
callq 0x1d40e
subq $-0x80, %r14
movq %r14, %rdi
movq %rax, %rsi
movq %rbp, %rdx
callq 0x17c38
jmp 0x156ef
leaq 0x88(%rsp), %rax
movq %rax, -0x30(%rax)
movq $0x1, -0x28(%rax)
xorps %xmm0, %xmm0
movups %xmm0, -0x20(%rax)
movl $0x3f800000, -0x10(%rax) # imm = 0x3F800000
movups %xmm0, -0x8(%rax)
movq 0xa0(%rsp), %r14
testq %r14, %r14
je 0x15a1c
movl 0x8(%r14), %esi
movq %rbx, %rdi
callq 0x9c0e
movl 0x60(%rax), %eax
movl %eax, 0x8(%rsp)
movl 0xc(%r14), %ecx
movl %ecx, 0xc(%rsp)
incl %ecx
cmpl $0x2, %ecx
jb 0x15a0b
testl %eax, %eax
je 0x15a0b
andl $0x0, 0x4(%rsp)
movq 0x30(%rsp), %rdi
leaq 0xc(%rsp), %rsi
callq 0x1d98c
testq %rax, %rax
je 0x158bc
movl 0xc(%rax), %esi
jmp 0x158d5
movl 0xc(%rsp), %esi
movq %rbx, %rdi
callq 0x6bf0
movl 0x20(%rax), %esi
cmpl 0xc(%rsp), %esi
jne 0x15a13
movl %esi, 0x4(%rsp)
testl %esi, %esi
je 0x15a45
movq %rbx, %rdi
callq 0x6bf0
movq %rax, %rbp
leaq 0x8(%r14), %r13
movq 0x50(%rsp), %rdi
movq %r13, %rsi
callq 0x1e658
movq %rax, %r15
movq %rax, %rdi
leaq 0x4(%rsp), %rsi
callq 0x1aeea
testq %rax, %rax
setne %r12b
movb $0x1, 0x3(%rsp)
movl 0x8(%rsp), %ecx
cmpl 0x4(%rsp), %ecx
je 0x15993
movq %r15, %rdi
leaq 0x8(%rsp), %rsi
callq 0x1aeea
testq %rax, %rax
setne %al
movl 0x8(%rsp), %edx
movq 0x18(%rsp), %rcx
movq 0x28(%rcx), %rsi
imulq $0x18, %rdx, %rdi
movq (%rsi,%rdi), %rcx
movq 0x8(%rsi,%rdi), %rsi
subq %rcx, %rsi
cmpq $0x4, %rsi
jne 0x15a0b
movl (%rcx), %ecx
movq 0x18(%rsp), %rsi
movq 0x10(%rsi), %rdi
imulq $0x18, %rcx, %r8
movq (%rdi,%r8), %rsi
movq 0x8(%rdi,%r8), %rdi
subq %rsi, %rdi
cmpq $0x4, %rdi
jne 0x15a0b
cmpl %edx, (%rsi)
jne 0x15a0b
orb %al, %r12b
movl %ecx, 0x8(%rsp)
jmp 0x1591d
andb 0x3(%rsp), %r12b
testb $0x1, %r12b
je 0x15a0b
leaq 0x58(%rsp), %r12
movq %r12, %rdi
callq 0xb3ca
movl 0x1c(%rbp), %edx
movq %r15, 0x38(%rsp)
leaq 0x3(%rsp), %rax
movq %rax, 0x40(%rsp)
movq 0x18(%rsp), %rdi
movq %r12, %rsi
leaq 0x38(%rsp), %rcx
callq 0x15ab2
cmpb $0x0, 0x3(%rsp)
je 0x15a0b
movq %rbp, %rdi
addq $0xd0, %rdi
movq %r13, %rsi
callq 0x6c06
movq 0xd0(%rbp), %rdi
movq 0xd8(%rbp), %rsi
callq 0x1cabb
movl (%r13), %esi
movq %rbx, %rdi
callq 0x9c0e
movb $0x1, 0x64(%rax)
movq (%r14), %r14
jmp 0x15867
movl 0x4(%rsp), %esi
jmp 0x158d9
leaq 0x58(%rsp), %rdi
callq 0xb51c
leaq 0x90(%rsp), %rdi
callq 0xbbf6
addq $0xc8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x7a5f0(%rip), %rdi     # 0x9003c
leaq 0x7a286(%rip), %rsi     # 0x8fcd9
leaq 0x7a5e9(%rip), %rcx     # 0x90043
movl $0xbd2, %edx            # imm = 0xBD2
callq 0x61c0
jmp 0x15a88
jmp 0x15a88
jmp 0x15a99
jmp 0x15a99
jmp 0x15a88
jmp 0x15a88
jmp 0x15a88
jmp 0x15a99
jmp 0x15a99
jmp 0x15a99
jmp 0x15a88
jmp 0x15a99
jmp 0x15a88
jmp 0x15a99
jmp 0x15a99
jmp 0x15a99
jmp 0x15a99
jmp 0x15a99
movq %rax, %rbx
leaq 0x58(%rsp), %rdi
callq 0xb51c
jmp 0x15a9c
jmp 0x15a99
movq %rax, %rbx
leaq 0x90(%rsp), %rdi
callq 0xbbf6
movq %rbx, %rdi
callq 0x64b0
nop