package main

when ODIN_OS == .Windows {
	foreign import llvmc "llvm/win64/LLVM-C.lib"
} else when ODIN_OS == .Linux || ODIN_OS == .Darwin {
	foreign import llvmc "system:LLVMCore"
}

LLVMBool :: distinct int
LLVMMemoryBufferRef :: distinct rawptr
LLVMContextRef :: distinct rawptr
LLVMModuleRef :: distinct rawptr
LLVMTypeRef :: distinct rawptr
LLVMValueRef :: distinct rawptr
LLVMBasicBlockRef :: distinct rawptr
LLVMMetadataRef :: distinct rawptr
LLVMNamedMDNodeRef :: distinct rawptr
LLVMBuilderRef :: distinct rawptr
LLVMDIBuilderRef :: distinct rawptr
LLVMModuleProviderRef :: distinct rawptr
LLVMPassManagerRef :: distinct rawptr
LLVMUseRef :: distinct rawptr
LLVMAttributeRef :: distinct rawptr
LLVMDiagnosticInfoRef :: distinct rawptr
LLVMComdatRef :: distinct rawptr
LLVMJITEventListenerRef :: distinct rawptr
LLVMBinaryRef :: distinct rawptr

LLVMnnamedAddr :: enum {
	NoUnnamedAddr,
	LocalUnnamedAddr,
	GlobalUnnamedAddr,
}

LLVMTypeKind :: enum {
	LLVMVoidTypeKind,
	LLVMHalfTypeKind,
	LLVMFloatTypeKind,
	LLVMDoubleTypeKind,
	LLVMX86_FP80TypeKind,
	LLVMFP128TypeKind,
	LLVMPPC_FP128TypeKind,
	LLVMLabelTypeKind,
	LLVMIntegerTypeKind,
	LLVMFunctionTypeKind,
	LLVMStructTypeKind,
	LLVMArrayTypeKind,
	LLVMPointerTypeKind,
	LLVMVectorTypeKind,
	LLVMMetadataTypeKind,
	LLVMX86_MMXTypeKind,
	LLVMTokenTypeKind,
	LLVMScalableVectorTypeKind,
	LLVMBFloatTypeKind,
	LLVMX86_AMXTypeKind,
	LLVMTargetExtTypeKind,
}

LLVMLinkage :: enum {
	ExternalLinkage,
	AvailableExternallyLinkage,
	LinkOnceAnyLinkage,
	LinkOnceODRLinkage,
	LinkOnceODRAutoHideLinkage,
	WeakAnyLinkage,
	WeakODRLinkage,
	AppendingLinkage,
	InternalLinkage,
	PrivateLinkage,
	DLLImportLinkage,
	DLLExportLinkage,
	ExternalWeakLinkage,
	GhostLinkage,
	CommonLinkage,
	LinkerPrivateLinkage,
	LinkerPrivateWeakLinkage,
}

LLVMIntPredicate :: enum {
	LLVMIntEQ = 32,
	LLVMIntNE,
	LLVMIntUGT,
	LLVMIntUGE,
	LLVMIntULT,
	LLVMIntULE,
	LLVMIntSGT,
	LLVMIntSGE,
	LLVMIntSLT,
	LLVMIntSLE,
}

LLVMRealPredicate :: enum {
	LLVMRealPredicateFalse,
	LLVMRealOEQ,
	LLVMRealOGT,
	LLVMRealOGE,
	LLVMRealOLT,
	LLVMRealOLE,
	LLVMRealONE,
	LLVMRealORD,
	LLVMRealUNO,
	LLVMRealUEQ,
	LLVMRealUGT,
	LLVMRealUGE,
	LLVMRealULT,
	LLVMRealULE,
	LLVMRealUNE,
	LLVMRealPredicateTrue,
}

@(default_calling_convention = "c")
foreign llvmc {
	LLVMContextCreate :: proc() -> LLVMContextRef ---
	LLVMModuleCreateWithName :: proc(name: cstring) -> LLVMModuleRef ---
	LLVMModuleCreateWithNameInContext :: proc(name: cstring, C: LLVMContextRef) -> LLVMModuleRef ---

	LLVMCreateBuilderInContext :: proc(C: LLVMContextRef) -> LLVMBuilderRef ---
	LLVMPositionBuilderAtEnd :: proc(Builder: LLVMBuilderRef, Block: LLVMBasicBlockRef) ---

	LLVMBuildPointerCast :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildGlobalString :: proc(Builder: LLVMBuilderRef, Str: cstring, Name: cstring) -> LLVMValueRef ---
	LLVMBuildGlobalStringPtr :: proc(Builder: LLVMBuilderRef, Str: cstring, Name: cstring) -> LLVMValueRef ---

	LLVMBuildCall2 :: proc(Builder: LLVMBuilderRef, Type: LLVMTypeRef, Fn: LLVMValueRef, Args: [^]LLVMValueRef, NumArgs: uint, Name: cstring) -> LLVMValueRef ---

	LLVMBuildRetVoid :: proc(Builder: LLVMBuilderRef) -> LLVMValueRef ---
	LLVMBuildRet :: proc(Builder: LLVMBuilderRef, V: LLVMValueRef) -> LLVMValueRef ---

	LLVMConstString :: proc(Str: cstring, Length: uint, DontNullTerminate: LLVMBool) -> LLVMValueRef ---
	LLVMInt8Type :: proc() -> LLVMTypeRef ---
	LLVMArrayType :: proc(ElementType: LLVMTypeRef, ElementCount: u64) -> LLVMTypeRef ---
	LLVMInt64Type :: proc() -> LLVMTypeRef ---

	LLVMInt1TypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---
	LLVMInt8TypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---
	LLVMInt16TypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---
	LLVMInt32TypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---
	LLVMInt64TypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---
	LLVMIntTypeInContext :: proc(C: LLVMContextRef, n: uint) -> LLVMTypeRef ---

	LLVMDoubleTypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---
	LLVMFloatTypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---

	LLVMVoidTypeInContext :: proc(C: LLVMContextRef) -> LLVMTypeRef ---
	LLVMVoidType :: proc() -> LLVMTypeRef ---

	LLVMGetTypeKind :: proc(Ty: LLVMTypeRef) -> LLVMTypeKind ---

	LLVMArrayType2 :: proc(ElementType: LLVMTypeRef, ElementCount: u64) -> LLVMTypeRef ---
	LLVMPointerType :: proc(ElementType: LLVMTypeRef, AddressSpace: uint) -> LLVMTypeRef ---

	LLVMConstInt :: proc(IntTy: LLVMTypeRef, N: u64, SignExtend: LLVMBool) -> LLVMValueRef ---
	LLVMConstReal :: proc(RealTy: LLVMTypeRef, N: f64) -> LLVMValueRef ---
	LLVMConstNamedStruct :: proc(StructTy: LLVMTypeRef, ConstantVals: [^]LLVMValueRef, Count: uint) -> LLVMValueRef ---

	LLVMStructTypeInContext :: proc(C: LLVMContextRef, ElementTypes: [^]LLVMTypeRef, ElementCount: uint, Packed: LLVMBool) -> LLVMTypeRef ---
	LLVMStructCreateNamed :: proc(C: LLVMContextRef, Name: cstring) -> LLVMTypeRef ---
	LLVMStructSetBody :: proc(StructTy: LLVMTypeRef, ElementTypes: [^]LLVMTypeRef, ElementCount: uint, Packed: LLVMBool) ---

	LLVMFunctionType :: proc(ReturnType: LLVMTypeRef, ParamTypes: [^]LLVMTypeRef, ParamCount: uint, IsVarArg: LLVMBool) -> LLVMTypeRef ---
	LLVMAddFunction :: proc(M: LLVMModuleRef, Name: cstring, FunctionType: LLVMTypeRef) -> LLVMValueRef ---

	LLVMAppendBasicBlockInContext :: proc(C: LLVMContextRef, Fn: LLVMValueRef, Name: cstring) -> LLVMBasicBlockRef ---
	LLVMCreateBasicBlockInContext :: proc(C: LLVMContextRef, Name: cstring) -> LLVMBasicBlockRef ---
	LLVMGetLastBasicBlock :: proc(Fn: LLVMValueRef) -> LLVMBasicBlockRef ---
	LLVMGetInsertBlock :: proc(Builder: LLVMBuilderRef) -> LLVMBasicBlockRef ---
	LLVMInsertExistingBasicBlockAfterInsertBlock :: proc(Builder: LLVMBuilderRef, Block: LLVMBasicBlockRef) ---
	LLVMAppendExistingBasicBlock :: proc(Fn: LLVMValueRef, Block: LLVMBasicBlockRef) ---

	LLVMPrintModuleToFile :: proc(M: LLVMModuleRef, Filename: cstring, ErrorMessage: ^cstring) -> LLVMBool ---

	LLVMGetValueName2 :: proc(V: LLVMValueRef, Length: ^u64) -> cstring ---

	LLVMBuildFAdd :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildAdd :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFSub :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildSub :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFMul :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildMul :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFDiv :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildUDiv :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildSDiv :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFRem :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildURem :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildSRem :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---

	LLVMBuildAlloca :: proc(Builder: LLVMBuilderRef, Ty: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildStore :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, Ptr: LLVMValueRef) -> LLVMValueRef ---
	LLVMBuildLoad2 :: proc(Builder: LLVMBuilderRef, Ty: LLVMTypeRef, PointerVal: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildBr :: proc(Builder: LLVMBuilderRef, Dest: LLVMBasicBlockRef) -> LLVMValueRef ---
	LLVMBuildCondBr :: proc(Builder: LLVMBuilderRef, If: LLVMValueRef, Then: LLVMBasicBlockRef, Else: LLVMBasicBlockRef) -> LLVMValueRef ---
	LLVMBuildICmp :: proc(Builder: LLVMBuilderRef, Predicate: LLVMIntPredicate, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFCmp :: proc(Builder: LLVMBuilderRef, Predicate: LLVMRealPredicate, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---

	LLVMBuildAnd :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildOr :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildXor :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildNot :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildShl :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildLShr :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildAShr :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildShr :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, RHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---

	LLVMBuildNeg :: proc(Builder: LLVMBuilderRef, LHS: LLVMValueRef, Name: cstring) -> LLVMValueRef ---

	LLVMBuildPhi :: proc(Builder: LLVMBuilderRef, Ty: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---

	LLVMBuildFPToSI :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFPToUI :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildSExt :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildZExt :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildIntCast :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildSIToFP :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFPTrunc :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildFPExt :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMBuildBitCast :: proc(Builder: LLVMBuilderRef, Val: LLVMValueRef, DestTy: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---

	LLVMBuildStructGEP2 :: proc(Builder: LLVMBuilderRef, Ty: LLVMTypeRef, Pointer: LLVMValueRef, Idx: uint, Name: cstring) -> LLVMValueRef ---

	LLVMTypeOf :: proc(Val: LLVMValueRef) -> LLVMTypeRef ---

	LLVMAddIncoming :: proc(PhiNode: LLVMValueRef, IncomingValues: [^]LLVMValueRef, IncomingBlocks: [^]LLVMBasicBlockRef, Count: uint) ---

	LLVMDisposeBuilder :: proc(Builder: LLVMBuilderRef) ---
	LLVMDisposeModule :: proc(M: LLVMModuleRef) ---
	LLVMContextDispose :: proc(C: LLVMContextRef) ---

	LLVMGetParam :: proc(Fn: LLVMValueRef, Index: uint) -> LLVMValueRef ---
	LLVMSetValueName2 :: proc(Val: LLVMValueRef, Name: cstring, Length: u64) ---

	LLVMGetReturnType :: proc(Fn: LLVMTypeRef) -> LLVMTypeRef ---

	LLVMAddGlobal :: proc(M: LLVMModuleRef, Ty: LLVMTypeRef, Name: cstring) -> LLVMValueRef ---
	LLVMSetInitializer :: proc(GlobalVar: LLVMValueRef, ConstantVal: LLVMValueRef) ---
	LLVMSetGlobalConstant :: proc(GlobalVar: LLVMValueRef, Val: LLVMBool) ---
	LLVMSetLinkage :: proc(GlobalVar: LLVMValueRef, Linkage: LLVMLinkage) ---
	LLVMSetUnnamedAddress :: proc(GlobalVar: LLVMValueRef, UnnamedAddress: LLVMnnamedAddr) ---
	LLVMSetAlignment :: proc(GlobalVar: LLVMValueRef, Bytes: uint) ---
	LLVMBuildInBoundsGEP2 :: proc(Builder: LLVMBuilderRef, Ty: LLVMTypeRef, Pointer: LLVMValueRef, Indices: [^]LLVMValueRef, NumIndices: uint, Name: cstring) -> LLVMValueRef ---
}
