{% skip_file unless flag?(:x86_64) && !flag?(:win32) %}
require "../../../spec_helper"

describe "Code gen: C ABI x86_64" do
  it "passes struct less than 64 bits as { i64 }" do
    mod = codegen(<<-CRYSTAL)
      lib LibFoo
        struct Struct
          x : Int8
          y : Int16
        end

        fun foo(s : Struct)
      end

      s = LibFoo::Struct.new
      LibFoo.foo(s)
      CRYSTAL
    str = mod.to_s
    str.should contain("call void @foo({ i64 }")
    str.should contain("declare void @foo({ i64 })")
  end

  it "passes struct less than 64 bits as { i64 } in varargs" do
    mod = codegen(<<-CRYSTAL)
      lib LibFoo
        struct Struct
          x : Int8
          y : Int16
        end

        fun foo(...)
      end

      s = LibFoo::Struct.new
      LibFoo.foo(s)
      CRYSTAL
    str = mod.to_s
    str.should contain("call void (...)")
  end

  it "passes struct between 64 and 128 bits as { i64, i64 }" do
    mod = codegen(<<-CRYSTAL)
      lib LibFoo
        struct Struct
          x : Int64
          y : Int16
        end

        fun foo(s : Struct)
      end

      s = LibFoo::Struct.new
      LibFoo.foo(s)
      CRYSTAL
    str = mod.to_s
    str.should contain("call void @foo({ i64, i64 }")
    str.should contain("declare void @foo({ i64, i64 })")
  end

  it "passes struct between 64 and 128 bits as { i64, i64 } (with multiple modules/contexts)" do
    codegen(<<-CRYSTAL)
      require "prelude"

      lib LibFoo
        struct Struct
          x : Int64
          y : Int16
        end

        fun foo(s : Struct)
      end

      module Moo
        def self.moo
          s = LibFoo::Struct.new
          LibFoo.foo(s)
        end
      end

      Moo.moo
      CRYSTAL
  end

  it "passes struct bigger than128 bits with byval" do
    mod = codegen(<<-CRYSTAL)
      lib LibFoo
        struct Struct
          x : Int64
          y : Int64
          z : Int8
        end

        fun foo(s : Struct)
      end

      s = LibFoo::Struct.new
      LibFoo.foo(s)
      CRYSTAL
    str = mod.to_s
    str.scan(/byval/).size.should eq(2)
  end

  it "returns struct less than 64 bits as { i64 }" do
    mod = codegen(<<-CRYSTAL)
      lib LibFoo
        struct Struct
          x : Int8
          y : Int16
        end

        fun foo : Struct
      end

      str = LibFoo.foo
      CRYSTAL
    str = mod.to_s
    str.should contain("call { i64 } @foo()")
    str.should contain("declare { i64 } @foo()")
  end

  it "returns struct between 64 and 128 bits as { i64, i64 }" do
    mod = codegen(<<-CRYSTAL)
      lib LibFoo
        struct Struct
          x : Int64
          y : Int16
        end

        fun foo : Struct
      end

      str = LibFoo.foo
      CRYSTAL
    str = mod.to_s
    str.should contain("call { i64, i64 } @foo()")
    str.should contain("declare { i64, i64 } @foo()")
  end

  it "returns struct bigger than 128 bits with sret" do
    mod = codegen(<<-CRYSTAL)
      lib LibFoo
        struct Struct
          x : Int64
          y : Int64
          z : Int8
        end

        fun foo(w : Int32) : Struct
      end

      str = LibFoo.foo(1)
      CRYSTAL
    str = mod.to_s
    str.scan(/sret/).size.should eq(2)

    if LibLLVM::IS_LT_120
      str.should contain("sret, i32") # sret goes as first argument
    else
      str.should contain("sret(%\"struct.LibFoo::Struct\") %0, i32") # sret goes as first argument
    end
  end
end
