// D import file generated from '../../../../libphobos/libdruntime/core/stdc/stdarg.d'
module core.stdc.stdarg;
@system version (GNU)
{
	import gcc.builtins;
	alias __builtin_va_list __gnuc_va_list;
	alias __gnuc_va_list va_list;
	template va_start(T)
	{
		void va_start(out va_list ap, ref T parmn);

	}
	template va_arg(T)
	{
		T va_arg(ref va_list ap);

	}
	template va_arg(T)
	{
		void va_arg(ref va_list ap, ref T parmn);

	}
	version (X86_64)
	{
		struct __va_list
		{
			uint offset_regs = 6 * 8;
			uint offset_fpregs = 6 * 8 + 8 * 16;
			void* stack_args;
			void* reg_args;
		}
		template va_arg()
		{
			void va_arg(ref va_list apx, TypeInfo ti, void* parmn)
			{
				__va_list* ap = cast(__va_list*)apx;
				TypeInfo arg1,arg2;
				if (!ti.argTypes(arg1, arg2))
				{
					bool inXMMregister(TypeInfo arg);
					TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector)arg1 : null;
					if (arg1 && (arg1.tsize() <= 8 || v1))
					{
						auto tsize = arg1.tsize();
						void* p;
						bool stack = false;
						auto offset_fpregs_save = ap.offset_fpregs;
						auto offset_regs_save = ap.offset_regs;
						L1:
						if (inXMMregister(arg1) || v1)
						{
							if (ap.offset_fpregs < 6 * 8 + 16 * 8 && !stack)
							{
								p = ap.reg_args + ap.offset_fpregs;
								ap.offset_fpregs += 16;
							}
							else
							{
								p = ap.stack_args;
								ap.stack_args += tsize + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
								stack = true;
							}
						}
						else
						{
							if (ap.offset_regs < 6 * 8 && !stack)
							{
								p = ap.reg_args + ap.offset_regs;
								ap.offset_regs += 8;
							}
							else
							{
								p = ap.stack_args;
								ap.stack_args += 8;
								stack = true;
							}
						}
						parmn[0..tsize] = p[0..tsize];
						if (arg2)
						{
							if (inXMMregister(arg2))
							{
								if (ap.offset_fpregs < 6 * 8 + 16 * 8 && !stack)
								{
									p = ap.reg_args + ap.offset_fpregs;
									ap.offset_fpregs += 16;
								}
								else
								{
									if (!stack)
									{
										ap.offset_fpregs = offset_fpregs_save;
										ap.offset_regs = offset_regs_save;
										stack = true;
										goto L1;
									}
									p = ap.stack_args;
									ap.stack_args += arg2.tsize() + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
								}
							}
							else
							{
								if (ap.offset_regs < 6 * 8 && !stack)
								{
									p = ap.reg_args + ap.offset_regs;
									ap.offset_regs += 8;
								}
								else
								{
									if (!stack)
									{
										ap.offset_fpregs = offset_fpregs_save;
										ap.offset_regs = offset_regs_save;
										stack = true;
										goto L1;
									}
									p = ap.stack_args;
									ap.stack_args += 8;
								}
							}
							auto sz = ti.tsize() - 8;
							(parmn + 8)[0..sz] = p[0..sz];
						}
					}
					else
					{
						auto talign = ti.talign();
						auto tsize = ti.tsize();
						auto p = cast(void*)(cast(size_t)ap.stack_args + talign - 1 & ~(talign - 1));
						ap.stack_args = cast(void*)(cast(size_t)p + (tsize + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
						parmn[0..tsize] = p[0..tsize];
					}
				}
				else
				{
					assert(false,"not a valid argument type for va_arg");
				}
			}

		}
	}
	else
	{
		version (ARM)
		{
			template va_arg()
			{
				void va_arg(ref va_list ap, TypeInfo ti, void* parmn)
				{
					auto p = *cast(void**)&ap;
					auto tsize = ti.tsize();
					*cast(void**)&ap += tsize + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
					parmn[0..tsize] = p[0..tsize];
				}

			}
		}
		else
		{
			template va_arg()
			{
				void va_arg(ref va_list ap, TypeInfo ti, void* parmn)
				{
					static assert(false,"Unsupported platform");
				}

			}
		}
	}
	alias __builtin_va_end va_end;
	alias __builtin_va_copy va_copy;
}
else
{
	version (X86)
	{
		alias void* va_list;
		template va_start(T)
		{
			void va_start(out va_list ap, ref T parmn)
			{
				ap = cast(va_list)(cast(void*)&parmn + (T.sizeof + (int).sizeof - 1 & ~((int).sizeof - 1)));
			}

		}
		template va_arg(T)
		{
			T va_arg(ref va_list ap)
			{
				T arg = *cast(T*)ap;
				ap = cast(va_list)(cast(void*)ap + (T.sizeof + (int).sizeof - 1 & ~((int).sizeof - 1)));
				return arg;
			}

		}
		template va_arg(T)
		{
			void va_arg(ref va_list ap, ref T parmn)
			{
				parmn = *cast(T*)ap;
				ap = cast(va_list)(cast(void*)ap + (T.sizeof + (int).sizeof - 1 & ~((int).sizeof - 1)));
			}

		}
		template va_arg()
		{
			void va_arg(ref va_list ap, TypeInfo ti, void* parmn)
			{
				auto p = ap;
				auto tsize = ti.tsize;
				ap = cast(void*)(cast(size_t)p + (tsize + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
				parmn[0..tsize] = p[0..tsize];
			}

		}
		void va_end(va_list ap);
		void va_copy(out va_list dest, va_list src);
	}
	else
	{
		version (Windows)
		{
			alias void* va_list;
			template va_start(T)
			{
				void va_start(out va_list ap, ref T parmn)
				{
					ap = cast(va_list)(cast(void*)&parmn + (size_t.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
				}

			}
			template va_arg(T)
			{
				T va_arg(ref va_list ap)
				{
					static if(T.sizeof > size_t.sizeof)
					{
						T arg = **cast(T**)ap;
					}
					else
					{
						T arg = *cast(T*)ap;
					}

					ap = cast(va_list)(cast(void*)ap + (size_t.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
					return arg;
				}

			}
			template va_arg(T)
			{
				void va_arg(ref va_list ap, ref T parmn)
				{
					static if(T.sizeof > size_t.sizeof)
					{
						parmn = **cast(T**)ap;
					}
					else
					{
						parmn = *cast(T*)ap;
					}

					ap = cast(va_list)(cast(void*)ap + (size_t.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
				}

			}
			template va_arg()
			{
				void va_arg(ref va_list ap, TypeInfo ti, void* parmn)
				{
					auto p = ap;
					auto tsize = ti.tsize;
					ap = cast(void*)(cast(size_t)p + (size_t.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
					void* q = tsize > size_t.sizeof ? *cast(void**)p : p;
					parmn[0..tsize] = q[0..tsize];
				}

			}
			void va_end(va_list ap);
			void va_copy(out va_list dest, va_list src);
		}
		else
		{
			version (X86_64)
			{
				template isVectorType(T)
				{
					enum isVectorType = false;
				}
				template isVectorType(T : __vector(T[N]), size_t N)
				{
					enum isVectorType = true;
				}
				struct __va_list
				{
					uint offset_regs = 6 * 8;
					uint offset_fpregs = 6 * 8 + 8 * 16;
					void* stack_args;
					void* reg_args;
				}
				align (16)struct __va_argsave_t
				{
					size_t[6] regs;
					real[8] fpregs;
					__va_list va;
				}

				alias void* va_list;
				template va_start(T)
				{
					void va_start(out va_list ap, ref T parmn)
					{
						ap = &parmn.va;
					}

				}
				template va_arg(T)
				{
					T va_arg(va_list ap)
					{
						T a;
						va_arg(ap, a);
						return a;
					}

				}
				template va_arg(T)
				{
					void va_arg(va_list apx, ref T parmn)
					{
						__va_list* ap = cast(__va_list*)apx;
						static if(is(T U == __argTypes))
						{
							static if(U.length == 0 || T.sizeof > 16 || U[0].sizeof > 8 && !isVectorType!(U[0]))
							{
								auto p = cast(size_t)ap.stack_args + T.alignof - 1 & ~(T.alignof - 1);
								ap.stack_args = cast(void*)(p + (T.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
								parmn = *cast(T*)p;
							}
							else
							{
								static if(U.length == 1)
								{
									alias U[0] T1;
									static if(is(T1 == double) || is(T1 == float) || isVectorType!(T1))
									{
										if (ap.offset_fpregs < 6 * 8 + 16 * 8)
										{
											parmn = *cast(T*)(ap.reg_args + ap.offset_fpregs);
											ap.offset_fpregs += 16;
										}
										else
										{
											parmn = *cast(T*)ap.stack_args;
											ap.stack_args += T1.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
										}
									}
									else
									{
										if (ap.offset_regs < 6 * 8 && T.sizeof <= 8)
										{
											parmn = *cast(T*)(ap.reg_args + ap.offset_regs);
											ap.offset_regs += 8;
										}
										else
										{
											auto p = cast(size_t)ap.stack_args + T.alignof - 1 & ~(T.alignof - 1);
											ap.stack_args = cast(void*)(p + (T.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
											parmn = *cast(T*)p;
										}
									}

								}
								else
								{
									static if(U.length == 2)
									{
										alias U[0] T1;
										alias U[1] T2;
										auto p = cast(void*)&parmn + 8;
										static if((is(T1 == double) || is(T1 == float)) && (is(T2 == double) || is(T2 == float)))
										{
											if (ap.offset_fpregs < 6 * 8 + 16 * 8 - 16)
											{
												*cast(T1*)&parmn = *cast(T1*)(ap.reg_args + ap.offset_fpregs);
												*cast(T2*)p = *cast(T2*)(ap.reg_args + ap.offset_fpregs + 16);
												ap.offset_fpregs += 32;
											}
											else
											{
												*cast(T1*)&parmn = *cast(T1*)ap.stack_args;
												ap.stack_args += T1.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
												*cast(T2*)p = *cast(T2*)ap.stack_args;
												ap.stack_args += T2.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
											}
										}
										else
										{
											static if(is(T1 == double) || is(T1 == float))
											{
												void* a = void;
												if (ap.offset_fpregs < 6 * 8 + 16 * 8 && ap.offset_regs < 6 * 8 && T2.sizeof <= 8)
												{
													*cast(T1*)&parmn = *cast(T1*)(ap.reg_args + ap.offset_fpregs);
													ap.offset_fpregs += 16;
													a = ap.reg_args + ap.offset_regs;
													ap.offset_regs += 8;
												}
												else
												{
													*cast(T1*)&parmn = *cast(T1*)ap.stack_args;
													ap.stack_args += T1.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
													a = ap.stack_args;
													ap.stack_args += 8;
												}
												const sz2 = T.sizeof - 8;
												p[0..sz2] = a[0..sz2];
											}
											else
											{
												static if(is(T2 == double) || is(T2 == float))
												{
													if (ap.offset_regs < 6 * 8 && T1.sizeof <= 8 && ap.offset_fpregs < 6 * 8 + 16 * 8)
													{
														*cast(T1*)&parmn = *cast(T1*)(ap.reg_args + ap.offset_regs);
														ap.offset_regs += 8;
														*cast(T2*)p = *cast(T2*)(ap.reg_args + ap.offset_fpregs);
														ap.offset_fpregs += 16;
													}
													else
													{
														*cast(T1*)&parmn = *cast(T1*)ap.stack_args;
														ap.stack_args += 8;
														*cast(T2*)p = *cast(T2*)ap.stack_args;
														ap.stack_args += T2.sizeof + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
													}
												}
												else
												{
													void* a = void;
													if (ap.offset_regs < 5 * 8 && T1.sizeof <= 8 && T2.sizeof <= 8)
													{
														*cast(T1*)&parmn = *cast(T1*)(ap.reg_args + ap.offset_regs);
														ap.offset_regs += 8;
														a = ap.reg_args + ap.offset_regs;
														ap.offset_regs += 8;
													}
													else
													{
														*cast(T1*)&parmn = *cast(T1*)ap.stack_args;
														ap.stack_args += 8;
														a = ap.stack_args;
														ap.stack_args += 8;
													}
													const sz2 = T.sizeof - 8;
													p[0..sz2] = a[0..sz2];
												}

											}

										}

									}
									else
									{
										static assert(false);
									}

								}

							}

						}
						else
						{
							static assert(false,"not a valid argument type for va_arg");
						}

					}

				}
				template va_arg()
				{
					void va_arg(va_list apx, TypeInfo ti, void* parmn)
					{
						__va_list* ap = cast(__va_list*)apx;
						TypeInfo arg1,arg2;
						if (!ti.argTypes(arg1, arg2))
						{
							bool inXMMregister(TypeInfo arg);
							TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector)arg1 : null;
							if (arg1 && (arg1.tsize <= 8 || v1))
							{
								auto tsize = arg1.tsize;
								void* p;
								bool stack = false;
								auto offset_fpregs_save = ap.offset_fpregs;
								auto offset_regs_save = ap.offset_regs;
								L1:
								if (inXMMregister(arg1) || v1)
								{
									if (ap.offset_fpregs < 6 * 8 + 16 * 8 && !stack)
									{
										p = ap.reg_args + ap.offset_fpregs;
										ap.offset_fpregs += 16;
									}
									else
									{
										p = ap.stack_args;
										ap.stack_args += tsize + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
										stack = true;
									}
								}
								else
								{
									if (ap.offset_regs < 6 * 8 && !stack)
									{
										p = ap.reg_args + ap.offset_regs;
										ap.offset_regs += 8;
									}
									else
									{
										p = ap.stack_args;
										ap.stack_args += 8;
										stack = true;
									}
								}
								parmn[0..tsize] = p[0..tsize];
								if (arg2)
								{
									if (inXMMregister(arg2))
									{
										if (ap.offset_fpregs < 6 * 8 + 16 * 8 && !stack)
										{
											p = ap.reg_args + ap.offset_fpregs;
											ap.offset_fpregs += 16;
										}
										else
										{
											if (!stack)
											{
												ap.offset_fpregs = offset_fpregs_save;
												ap.offset_regs = offset_regs_save;
												stack = true;
												goto L1;
											}
											p = ap.stack_args;
											ap.stack_args += arg2.tsize + size_t.sizeof - 1 & ~(size_t.sizeof - 1);
										}
									}
									else
									{
										if (ap.offset_regs < 6 * 8 && !stack)
										{
											p = ap.reg_args + ap.offset_regs;
											ap.offset_regs += 8;
										}
										else
										{
											if (!stack)
											{
												ap.offset_fpregs = offset_fpregs_save;
												ap.offset_regs = offset_regs_save;
												stack = true;
												goto L1;
											}
											p = ap.stack_args;
											ap.stack_args += 8;
										}
									}
									auto sz = ti.tsize - 8;
									(parmn + 8)[0..sz] = p[0..sz];
								}
							}
							else
							{
								auto talign = ti.talign;
								auto tsize = ti.tsize;
								auto p = cast(void*)(cast(size_t)ap.stack_args + talign - 1 & ~(talign - 1));
								ap.stack_args = cast(void*)(cast(size_t)p + (tsize + size_t.sizeof - 1 & ~(size_t.sizeof - 1)));
								parmn[0..tsize] = p[0..tsize];
							}
						}
						else
						{
							assert(false,"not a valid argument type for va_arg");
						}
					}

				}
				void va_end(va_list ap);
				void va_copy(out va_list dest, va_list src);
			}
			else
			{
				static assert(false,"Unsupported platform");
			}
		}
	}
}

