# This file is a part of Julia. License is MIT: https://julialang.org/license

# tests that interpreter matches codegen
using Test
using Core: GotoIfNot, ReturnNode

# test that interpreter correctly handles PhiNodes (#29262)
let m = Meta.@lower 1 + 1
    @assert Meta.isexpr(m, :thunk)
    src = m.args[1]::Core.CodeInfo
    src.code = Any[
        # block 1
        QuoteNode(:a),
        QuoteNode(:b),
        GlobalRef(@__MODULE__, :test29262),
        GotoIfNot(Core.SSAValue(3), 6),
        # block 2
        Core.PhiNode(Int32[4], Any[Core.SSAValue(1)]),
        Core.PhiNode(Int32[4, 5], Any[Core.SSAValue(2), Core.SSAValue(5)]),
        ReturnNode(Core.SSAValue(6)),
    ]
    nstmts = length(src.code)
    src.ssavaluetypes = Any[ Any for _ = 1:nstmts ]
    src.codelocs = fill(Int32(1), nstmts)
    src.inferred = true
    Core.Compiler.verify_ir(Core.Compiler.inflate_ir(src))
    global test29262 = true
    @test :a === @eval $m
    global test29262 = false
    @test :b === @eval $m
end

let m = Meta.@lower 1 + 1
    @assert Meta.isexpr(m, :thunk)
    src = m.args[1]::Core.CodeInfo
    src.code = Any[
        # block 1
        QuoteNode(:a),
        QuoteNode(:b),
        QuoteNode(:c),
        GlobalRef(@__MODULE__, :test29262),
        # block 2
        Core.PhiNode(Int32[4, 16], Any[false, true]), # false, true
        Core.PhiNode(Int32[4, 16], Any[Core.SSAValue(1), Core.SSAValue(2)]), # :a, :b
        Core.PhiNode(Int32[4, 16], Any[Core.SSAValue(3), Core.SSAValue(6)]), # :c, :a
        Core.PhiNode(Int32[16], Any[Core.SSAValue(7)]), # NULL, :c
        # block 3
        Core.PhiNode(Int32[], Any[]), # NULL, NULL
        Core.PhiNode(Int32[17, 8], Any[true, Core.SSAValue(4)]), # test29262, test29262, [true]
        Core.PhiNode(Int32[17], Vector{Any}(undef, 1)), # NULL, NULL
        Core.PhiNode(Int32[8], Vector{Any}(undef, 1)), # NULL, NULL
        Core.PhiNode(Int32[], Any[]), # NULL, NULL
        Core.PhiNode(Int32[17, 8], Any[Core.SSAValue(2), Core.SSAValue(8)]), # NULL, :c, [:b]
        Core.PhiNode(Int32[], Any[]), # NULL, NULL
        GotoIfNot(Core.SSAValue(5), 5),
        # block 4
        GotoIfNot(Core.SSAValue(10), 9),
        # block 5
        Expr(:call, GlobalRef(Core, :tuple), Core.SSAValue(6), Core.SSAValue(7), Core.SSAValue(8), Core.SSAValue(14)),
        ReturnNode(Core.SSAValue(18)),
    ]
    nstmts = length(src.code)
    src.ssavaluetypes = Any[ Any for _ = 1:nstmts ]
    src.codelocs = fill(Int32(1), nstmts)
    src.inferred = true
    Core.Compiler.verify_ir(Core.Compiler.inflate_ir(src))
    global test29262 = true
    @test (:b, :a, :c, :c) === @eval $m
    global test29262 = false
    @test (:b, :a, :c, :b) === @eval $m
end

let m = Meta.@lower 1 + 1
    @assert Meta.isexpr(m, :thunk)
    src = m.args[1]::Core.CodeInfo
    src.code = Any[
        # block 1
        QuoteNode(:a),
        QuoteNode(:b),
        GlobalRef(@__MODULE__, :test29262),
        # block 2
        Expr(:enter, 11),
        # block 3
        Core.UpsilonNode(),
        Core.UpsilonNode(),
        Core.UpsilonNode(Core.SSAValue(2)),
        GotoIfNot(Core.SSAValue(3), 10),
        # block 4
        Core.UpsilonNode(Core.SSAValue(1)),
        # block 5
        Expr(:throw_undef_if_not, :expected, false),
        # block 6
        Core.PhiCNode(Any[Core.SSAValue(5), Core.SSAValue(7), Core.SSAValue(9)]), # NULL, :a, :b
        Core.PhiCNode(Any[Core.SSAValue(6)]), # NULL
        Expr(:leave, 1),
        # block 7
        ReturnNode(Core.SSAValue(11)),
    ]
    nstmts = length(src.code)
    src.ssavaluetypes = Any[ Any for _ = 1:nstmts ]
    src.codelocs = fill(Int32(1), nstmts)
    src.inferred = true
    Core.Compiler.verify_ir(Core.Compiler.inflate_ir(src))
    global test29262 = true
    @test :a === @eval $m
    global test29262 = false
    @test :b === @eval $m
end

# https://github.com/JuliaLang/julia/issues/47065
# `Core.Compiler.sort!` should be able to handle a big list
let n = 1000
    ex = :(return 1)
    for _ in 1:n
        ex = :(rand() < .1 && $(ex))
    end
    @eval global function f_1000_blocks()
        $ex
        return 0
    end
end
@test f_1000_blocks() == 0
