Commit 563d9ebfe5

LemonBoy <thatlemon@gmail.com>
2019-12-23 21:52:06
Implement the callconv() annotation
1 parent 7bd80f2
doc/docgen.zig
@@ -818,6 +818,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
             .Keyword_resume,
             .Keyword_return,
             .Keyword_linksection,
+            .Keyword_callconv,
             .Keyword_stdcallcc,
             .Keyword_struct,
             .Keyword_suspend,
doc/langref.html.in
@@ -2829,7 +2829,7 @@ test "@tagName" {
       <p>
       By default, enums are not guaranteed to be compatible with the C ABI:
       </p>
-      {#code_begin|obj_err|parameter of type 'Foo' not allowed in function with calling convention 'ccc'#}
+      {#code_begin|obj_err|parameter of type 'Foo' not allowed in function with calling convention 'C'#}
 const Foo = enum { A, B, C };
 export fn entry(foo: Foo) void { }
       {#code_end#}
lib/std/os/linux/arm-eabi.zig
@@ -97,7 +97,7 @@ pub extern fn getThreadPointer() usize {
     );
 }
 
-pub nakedcc fn restore() void {
+pub fn restore() callconv(.Naked) void {
     return asm volatile ("svc #0"
         :
         : [number] "{r7}" (@as(usize, SYS_sigreturn))
@@ -105,7 +105,7 @@ pub nakedcc fn restore() void {
     );
 }
 
-pub nakedcc fn restore_rt() void {
+pub fn restore_rt() callconv(.Naked) void {
     return asm volatile ("svc #0"
         :
         : [number] "{r7}" (@as(usize, SYS_rt_sigreturn))
lib/std/os/linux/arm64.zig
@@ -90,7 +90,7 @@ pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, a
 
 pub const restore = restore_rt;
 
-pub nakedcc fn restore_rt() void {
+pub fn restore_rt() callconv(.Naked) void {
     return asm volatile ("svc #0"
         :
         : [number] "{x8}" (@as(usize, SYS_rt_sigreturn))
lib/std/os/linux/i386.zig
@@ -102,7 +102,7 @@ pub fn socketcall(call: usize, args: [*]usize) usize {
 /// This matches the libc clone function.
 pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
 
-pub nakedcc fn restore() void {
+pub fn restore() callconv(.Naked) void {
     return asm volatile ("int $0x80"
         :
         : [number] "{eax}" (@as(usize, SYS_sigreturn))
@@ -110,7 +110,7 @@ pub nakedcc fn restore() void {
     );
 }
 
-pub nakedcc fn restore_rt() void {
+pub fn restore_rt() callconv(.Naked) void {
     return asm volatile ("int $0x80"
         :
         : [number] "{eax}" (@as(usize, SYS_rt_sigreturn))
lib/std/os/linux/mipsel.zig
@@ -144,7 +144,7 @@ pub fn syscall6(
 /// This matches the libc clone function.
 pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
 
-pub nakedcc fn restore() void {
+pub fn restore() callconv(.Naked) void {
     return asm volatile ("syscall"
         :
         : [number] "{$2}" (@as(usize, SYS_sigreturn))
@@ -152,7 +152,7 @@ pub nakedcc fn restore() void {
     );
 }
 
-pub nakedcc fn restore_rt() void {
+pub fn restore_rt() callconv(.Naked) void {
     return asm volatile ("syscall"
         :
         : [number] "{$2}" (@as(usize, SYS_rt_sigreturn))
lib/std/os/linux/riscv64.zig
@@ -89,7 +89,7 @@ pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, a
 
 pub const restore = restore_rt;
 
-pub nakedcc fn restore_rt() void {
+pub fn restore_rt() callconv(.Naked) void {
     return asm volatile ("ecall"
         :
         : [number] "{x17}" (@as(usize, SYS_rt_sigreturn))
lib/std/os/linux/x86_64.zig
@@ -90,7 +90,7 @@ pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: usize,
 
 pub const restore = restore_rt;
 
-pub nakedcc fn restore_rt() void {
+pub fn restore_rt() callconv(.Naked) void {
     return asm volatile ("syscall"
         :
         : [number] "{rax}" (@as(usize, SYS_rt_sigreturn))
lib/std/os/uefi/protocols/simple_text_input_protocol.zig
@@ -27,4 +27,3 @@ pub const SimpleTextInputProtocol = extern struct {
         .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b },
     };
 };
-
lib/std/os/windows/advapi32.zig
@@ -1,23 +1,23 @@
 usingnamespace @import("bits.zig");
 
-pub extern "advapi32" stdcallcc fn RegOpenKeyExW(
+pub extern "advapi32" fn RegOpenKeyExW(
     hKey: HKEY,
     lpSubKey: LPCWSTR,
     ulOptions: DWORD,
     samDesired: REGSAM,
     phkResult: *HKEY,
-) LSTATUS;
+) callconv(.Stdcall) LSTATUS;
 
-pub extern "advapi32" stdcallcc fn RegQueryValueExW(
+pub extern "advapi32" fn RegQueryValueExW(
     hKey: HKEY,
     lpValueName: LPCWSTR,
     lpReserved: LPDWORD,
     lpType: LPDWORD,
     lpData: LPBYTE,
     lpcbData: LPDWORD,
-) LSTATUS;
+) callconv(.Stdcall) LSTATUS;
 
 // RtlGenRandom is known as SystemFunction036 under advapi32
 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */
-pub extern "advapi32" stdcallcc fn SystemFunction036(output: [*]u8, length: ULONG) BOOL;
+pub extern "advapi32" fn SystemFunction036(output: [*]u8, length: ULONG) callconv(.Stdcall) BOOL;
 pub const RtlGenRandom = SystemFunction036;
lib/std/os/windows/bits.zig
@@ -892,7 +892,7 @@ pub const EXCEPTION_POINTERS = extern struct {
     ContextRecord: *c_void,
 };
 
-pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_POINTERS) c_long;
+pub const VECTORED_EXCEPTION_HANDLER = fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(.Stdcall) c_long;
 
 pub const OBJECT_ATTRIBUTES = extern struct {
     Length: ULONG,
lib/std/os/windows/kernel32.zig
@@ -1,22 +1,22 @@
 usingnamespace @import("bits.zig");
 
-pub extern "kernel32" stdcallcc fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) ?*c_void;
-pub extern "kernel32" stdcallcc fn RemoveVectoredExceptionHandler(Handle: HANDLE) c_ulong;
+pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(.Stdcall) ?*c_void;
+pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(.Stdcall) c_ulong;
 
-pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
+pub extern "kernel32" fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
+pub extern "kernel32" fn CloseHandle(hObject: HANDLE) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
+pub extern "kernel32" fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn CreateEventExW(
+pub extern "kernel32" fn CreateEventExW(
     lpEventAttributes: ?*SECURITY_ATTRIBUTES,
     lpName: [*:0]const u16,
     dwFlags: DWORD,
     dwDesiredAccess: DWORD,
-) ?HANDLE;
+) callconv(.Stdcall) ?HANDLE;
 
-pub extern "kernel32" stdcallcc fn CreateFileW(
+pub extern "kernel32" fn CreateFileW(
     lpFileName: [*]const u16, // TODO null terminated pointer type
     dwDesiredAccess: DWORD,
     dwShareMode: DWORD,
@@ -24,16 +24,16 @@ pub extern "kernel32" stdcallcc fn CreateFileW(
     dwCreationDisposition: DWORD,
     dwFlagsAndAttributes: DWORD,
     hTemplateFile: ?HANDLE,
-) HANDLE;
+) callconv(.Stdcall) HANDLE;
 
-pub extern "kernel32" stdcallcc fn CreatePipe(
+pub extern "kernel32" fn CreatePipe(
     hReadPipe: *HANDLE,
     hWritePipe: *HANDLE,
     lpPipeAttributes: *const SECURITY_ATTRIBUTES,
     nSize: DWORD,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn CreateProcessW(
+pub extern "kernel32" fn CreateProcessW(
     lpApplicationName: ?LPWSTR,
     lpCommandLine: LPWSTR,
     lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
@@ -44,15 +44,15 @@ pub extern "kernel32" stdcallcc fn CreateProcessW(
     lpCurrentDirectory: ?LPWSTR,
     lpStartupInfo: *STARTUPINFOW,
     lpProcessInformation: *PROCESS_INFORMATION,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
+pub extern "kernel32" fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) callconv(.Stdcall) BOOLEAN;
 
-pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
+pub extern "kernel32" fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) callconv(.Stdcall) ?HANDLE;
 
-pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
+pub extern "kernel32" fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) callconv(.Stdcall) ?HANDLE;
 
-pub extern "kernel32" stdcallcc fn DeviceIoControl(
+pub extern "kernel32" fn DeviceIoControl(
     h: HANDLE,
     dwIoControlCode: DWORD,
     lpInBuffer: ?*const c_void,
@@ -61,107 +61,107 @@ pub extern "kernel32" stdcallcc fn DeviceIoControl(
     nOutBufferSize: DWORD,
     lpBytesReturned: ?*DWORD,
     lpOverlapped: ?*OVERLAPPED,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
+pub extern "kernel32" fn DeleteFileW(lpFileName: [*]const u16) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn DuplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: *HANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD) BOOL;
+pub extern "kernel32" fn DuplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: *HANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
+pub extern "kernel32" fn ExitProcess(exit_code: UINT) callconv(.Stdcall) noreturn;
 
-pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
-pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
-pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL;
+pub extern "kernel32" fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) callconv(.Stdcall) HANDLE;
+pub extern "kernel32" fn FindClose(hFindFile: HANDLE) callconv(.Stdcall) BOOL;
+pub extern "kernel32" fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn FormatMessageW(dwFlags: DWORD, lpSource: ?LPVOID, dwMessageId: DWORD, dwLanguageId: DWORD, lpBuffer: LPWSTR, nSize: DWORD, Arguments: ?*va_list) DWORD;
+pub extern "kernel32" fn FormatMessageW(dwFlags: DWORD, lpSource: ?LPVOID, dwMessageId: DWORD, dwLanguageId: DWORD, lpBuffer: LPWSTR, nSize: DWORD, Arguments: ?*va_list) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsW(penv: [*]u16) BOOL;
+pub extern "kernel32" fn FreeEnvironmentStringsW(penv: [*]u16) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
+pub extern "kernel32" fn GetCommandLineA() callconv(.Stdcall) LPSTR;
 
-pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
+pub extern "kernel32" fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
+pub extern "kernel32" fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
+pub extern "kernel32" fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
-pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD;
+pub extern "kernel32" fn GetCurrentThread() callconv(.Stdcall) HANDLE;
+pub extern "kernel32" fn GetCurrentThreadId() callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn GetEnvironmentStringsW() ?[*]u16;
+pub extern "kernel32" fn GetEnvironmentStringsW() callconv(.Stdcall) ?[*]u16;
 
-pub extern "kernel32" stdcallcc fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) DWORD;
+pub extern "kernel32" fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
+pub extern "kernel32" fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
+pub extern "kernel32" fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
+pub extern "kernel32" fn GetFileAttributesW(lpFileName: [*]const WCHAR) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
+pub extern "kernel32" fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
+pub extern "kernel32" fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) callconv(.Stdcall) HMODULE;
 
-pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
+pub extern "kernel32" fn GetLastError() callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn GetFileInformationByHandle(
+pub extern "kernel32" fn GetFileInformationByHandle(
     hFile: HANDLE,
     lpFileInformation: *BY_HANDLE_FILE_INFORMATION,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
+pub extern "kernel32" fn GetFileInformationByHandleEx(
     in_hFile: HANDLE,
     in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
     out_lpFileInformation: *c_void,
     in_dwBufferSize: DWORD,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
+pub extern "kernel32" fn GetFinalPathNameByHandleW(
     hFile: HANDLE,
     lpszFilePath: [*]u16,
     cchFilePath: DWORD,
     dwFlags: DWORD,
-) DWORD;
+) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
+pub extern "kernel32" fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
-pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
+pub extern "kernel32" fn GetProcessHeap() callconv(.Stdcall) ?HANDLE;
+pub extern "kernel32" fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
-pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
+pub extern "kernel32" fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) callconv(.Stdcall) void;
+pub extern "kernel32" fn GetSystemTimeAsFileTime(*FILETIME) callconv(.Stdcall) void;
 
-pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
-pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
-pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
-pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
-pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
-pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
+pub extern "kernel32" fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) callconv(.Stdcall) ?HANDLE;
+pub extern "kernel32" fn HeapDestroy(hHeap: HANDLE) callconv(.Stdcall) BOOL;
+pub extern "kernel32" fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) callconv(.Stdcall) ?*c_void;
+pub extern "kernel32" fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) callconv(.Stdcall) SIZE_T;
+pub extern "kernel32" fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) callconv(.Stdcall) SIZE_T;
+pub extern "kernel32" fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
+pub extern "kernel32" fn GetStdHandle(in_nStdHandle: DWORD) callconv(.Stdcall) ?HANDLE;
 
-pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
+pub extern "kernel32" fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) callconv(.Stdcall) ?*c_void;
 
-pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
+pub extern "kernel32" fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
+pub extern "kernel32" fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn VirtualAlloc(lpAddress: ?LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD) ?LPVOID;
-pub extern "kernel32" stdcallcc fn VirtualFree(lpAddress: ?LPVOID, dwSize: SIZE_T, dwFreeType: DWORD) BOOL;
+pub extern "kernel32" fn VirtualAlloc(lpAddress: ?LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD) callconv(.Stdcall) ?LPVOID;
+pub extern "kernel32" fn VirtualFree(lpAddress: ?LPVOID, dwSize: SIZE_T, dwFreeType: DWORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn MoveFileExW(
+pub extern "kernel32" fn MoveFileExW(
     lpExistingFileName: [*]const u16,
     lpNewFileName: [*]const u16,
     dwFlags: DWORD,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
+pub extern "kernel32" fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
+pub extern "kernel32" fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
+pub extern "kernel32" fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn ReadDirectoryChangesW(
+pub extern "kernel32" fn ReadDirectoryChangesW(
     hDirectory: HANDLE,
     lpBuffer: [*]align(@alignOf(FILE_NOTIFY_INFORMATION)) u8,
     nBufferLength: DWORD,
@@ -170,79 +170,79 @@ pub extern "kernel32" stdcallcc fn ReadDirectoryChangesW(
     lpBytesReturned: ?*DWORD,
     lpOverlapped: ?*OVERLAPPED,
     lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn ReadFile(
+pub extern "kernel32" fn ReadFile(
     in_hFile: HANDLE,
     out_lpBuffer: [*]u8,
     in_nNumberOfBytesToRead: DWORD,
     out_lpNumberOfBytesRead: ?*DWORD,
     in_out_lpOverlapped: ?*OVERLAPPED,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
+pub extern "kernel32" fn RemoveDirectoryW(lpPathName: [*]const u16) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
+pub extern "kernel32" fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn SetFilePointerEx(
+pub extern "kernel32" fn SetFilePointerEx(
     in_fFile: HANDLE,
     in_liDistanceToMove: LARGE_INTEGER,
     out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
     in_dwMoveMethod: DWORD,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn SetFileTime(
+pub extern "kernel32" fn SetFileTime(
     hFile: HANDLE,
     lpCreationTime: ?*const FILETIME,
     lpLastAccessTime: ?*const FILETIME,
     lpLastWriteTime: ?*const FILETIME,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
+pub extern "kernel32" fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
+pub extern "kernel32" fn Sleep(dwMilliseconds: DWORD) callconv(.Stdcall) void;
 
-pub extern "kernel32" stdcallcc fn SwitchToThread() BOOL;
+pub extern "kernel32" fn SwitchToThread() callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
+pub extern "kernel32" fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD;
+pub extern "kernel32" fn TlsAlloc() callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL;
+pub extern "kernel32" fn TlsFree(dwTlsIndex: DWORD) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
+pub extern "kernel32" fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn WaitForSingleObjectEx(hHandle: HANDLE, dwMilliseconds: DWORD, bAlertable: BOOL) DWORD;
+pub extern "kernel32" fn WaitForSingleObjectEx(hHandle: HANDLE, dwMilliseconds: DWORD, bAlertable: BOOL) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn WaitForMultipleObjects(nCount: DWORD, lpHandle: [*]const HANDLE, bWaitAll: BOOL, dwMilliseconds: DWORD) DWORD;
+pub extern "kernel32" fn WaitForMultipleObjects(nCount: DWORD, lpHandle: [*]const HANDLE, bWaitAll: BOOL, dwMilliseconds: DWORD) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn WaitForMultipleObjectsEx(
+pub extern "kernel32" fn WaitForMultipleObjectsEx(
     nCount: DWORD,
     lpHandle: [*]const HANDLE,
     bWaitAll: BOOL,
     dwMilliseconds: DWORD,
     bAlertable: BOOL,
-) DWORD;
+) callconv(.Stdcall) DWORD;
 
-pub extern "kernel32" stdcallcc fn WriteFile(
+pub extern "kernel32" fn WriteFile(
     in_hFile: HANDLE,
     in_lpBuffer: [*]const u8,
     in_nNumberOfBytesToWrite: DWORD,
     out_lpNumberOfBytesWritten: ?*DWORD,
     in_out_lpOverlapped: ?*OVERLAPPED,
-) BOOL;
+) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
+pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
+pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*]const u16) callconv(.Stdcall) ?HMODULE;
 
-pub extern "kernel32" stdcallcc fn GetProcAddress(hModule: HMODULE, lpProcName: [*]const u8) ?FARPROC;
+pub extern "kernel32" fn GetProcAddress(hModule: HMODULE, lpProcName: [*]const u8) callconv(.Stdcall) ?FARPROC;
 
-pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
+pub extern "kernel32" fn FreeLibrary(hModule: HMODULE) callconv(.Stdcall) BOOL;
 
-pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
-pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
-pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
-pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
+pub extern "kernel32" fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void;
+pub extern "kernel32" fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void;
+pub extern "kernel32" fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void;
+pub extern "kernel32" fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) callconv(.Stdcall) void;
 
-pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL;
+pub extern "kernel32" fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) callconv(.Stdcall) BOOL;
lib/std/os/windows/ntdll.zig
@@ -1,14 +1,14 @@
 usingnamespace @import("bits.zig");
 
-pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD;
-pub extern "NtDll" stdcallcc fn NtQueryInformationFile(
+pub extern "NtDll" fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) callconv(.Stdcall) WORD;
+pub extern "NtDll" fn NtQueryInformationFile(
     FileHandle: HANDLE,
     IoStatusBlock: *IO_STATUS_BLOCK,
     FileInformation: *c_void,
     Length: ULONG,
     FileInformationClass: FILE_INFORMATION_CLASS,
-) NTSTATUS;
-pub extern "NtDll" stdcallcc fn NtCreateFile(
+) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn NtCreateFile(
     FileHandle: *HANDLE,
     DesiredAccess: ACCESS_MASK,
     ObjectAttributes: *OBJECT_ATTRIBUTES,
@@ -20,8 +20,8 @@ pub extern "NtDll" stdcallcc fn NtCreateFile(
     CreateOptions: ULONG,
     EaBuffer: ?*c_void,
     EaLength: ULONG,
-) NTSTATUS;
-pub extern "NtDll" stdcallcc fn NtDeviceIoControlFile(
+) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn NtDeviceIoControlFile(
     FileHandle: HANDLE,
     Event: ?HANDLE,
     ApcRoutine: ?IO_APC_ROUTINE,
@@ -32,17 +32,17 @@ pub extern "NtDll" stdcallcc fn NtDeviceIoControlFile(
     InputBufferLength: ULONG,
     OutputBuffer: ?PVOID,
     OutputBufferLength: ULONG,
-) NTSTATUS;
-pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS;
-pub extern "NtDll" stdcallcc fn RtlDosPathNameToNtPathName_U(
+) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn NtClose(Handle: HANDLE) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn RtlDosPathNameToNtPathName_U(
     DosPathName: [*]const u16,
     NtPathName: *UNICODE_STRING,
     NtFileNamePart: ?*?[*]const u16,
     DirectoryInfo: ?*CURDIR,
-) BOOL;
-pub extern "NtDll" stdcallcc fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) void;
+) callconv(.Stdcall) BOOL;
+pub extern "NtDll" fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) callconv(.Stdcall) void;
 
-pub extern "NtDll" stdcallcc fn NtQueryDirectoryFile(
+pub extern "NtDll" fn NtQueryDirectoryFile(
     FileHandle: HANDLE,
     Event: ?HANDLE,
     ApcRoutine: ?IO_APC_ROUTINE,
@@ -54,22 +54,22 @@ pub extern "NtDll" stdcallcc fn NtQueryDirectoryFile(
     ReturnSingleEntry: BOOLEAN,
     FileName: ?*UNICODE_STRING,
     RestartScan: BOOLEAN,
-) NTSTATUS;
-pub extern "NtDll" stdcallcc fn NtCreateKeyedEvent(
+) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn NtCreateKeyedEvent(
     KeyedEventHandle: *HANDLE,
     DesiredAccess: ACCESS_MASK,
     ObjectAttributes: ?PVOID,
     Flags: ULONG,
-) NTSTATUS;
-pub extern "NtDll" stdcallcc fn NtReleaseKeyedEvent(
+) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn NtReleaseKeyedEvent(
     EventHandle: HANDLE,
     Key: *const c_void,
     Alertable: BOOLEAN,
     Timeout: ?*LARGE_INTEGER,
-) NTSTATUS;
-pub extern "NtDll" stdcallcc fn NtWaitForKeyedEvent(
+) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn NtWaitForKeyedEvent(
     EventHandle: HANDLE,
     Key: *const c_void,
     Alertable: BOOLEAN,
     Timeout: ?*LARGE_INTEGER,
-) NTSTATUS;
+) callconv(.Stdcall) NTSTATUS;
lib/std/os/windows/ole32.zig
@@ -1,6 +1,6 @@
 usingnamespace @import("bits.zig");
 
-pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
-pub extern "ole32" stdcallcc fn CoUninitialize() void;
-pub extern "ole32" stdcallcc fn CoGetCurrentProcess() DWORD;
-pub extern "ole32" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
+pub extern "ole32" fn CoTaskMemFree(pv: LPVOID) callconv(.Stdcall) void;
+pub extern "ole32" fn CoUninitialize() callconv(.Stdcall) void;
+pub extern "ole32" fn CoGetCurrentProcess() callconv(.Stdcall) DWORD;
+pub extern "ole32" fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) callconv(.Stdcall) HRESULT;
lib/std/os/windows/shell32.zig
@@ -1,3 +1,3 @@
 usingnamespace @import("bits.zig");
 
-pub extern "shell32" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
+pub extern "shell32" fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) callconv(.Stdcall) HRESULT;
lib/std/os/windows/ws2_32.zig
@@ -315,30 +315,30 @@ const IOC_WS2 = 0x08000000;
 
 pub const SIO_BASE_HANDLE = IOC_OUT | IOC_WS2 | 34;
 
-pub extern "ws2_32" stdcallcc fn WSAStartup(
+pub extern "ws2_32" fn WSAStartup(
     wVersionRequired: WORD,
     lpWSAData: *WSADATA,
-) c_int;
-pub extern "ws2_32" stdcallcc fn WSACleanup() c_int;
-pub extern "ws2_32" stdcallcc fn WSAGetLastError() c_int;
-pub extern "ws2_32" stdcallcc fn WSASocketA(
+) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSACleanup() callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSAGetLastError() callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSASocketA(
     af: c_int,
     type: c_int,
     protocol: c_int,
     lpProtocolInfo: ?*WSAPROTOCOL_INFOA,
     g: GROUP,
     dwFlags: DWORD,
-) SOCKET;
-pub extern "ws2_32" stdcallcc fn WSASocketW(
+) callconv(.Stdcall) SOCKET;
+pub extern "ws2_32" fn WSASocketW(
     af: c_int,
     type: c_int,
     protocol: c_int,
     lpProtocolInfo: ?*WSAPROTOCOL_INFOW,
     g: GROUP,
     dwFlags: DWORD,
-) SOCKET;
-pub extern "ws2_32" stdcallcc fn closesocket(s: SOCKET) c_int;
-pub extern "ws2_32" stdcallcc fn WSAIoctl(
+) callconv(.Stdcall) SOCKET;
+pub extern "ws2_32" fn closesocket(s: SOCKET) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSAIoctl(
     s: SOCKET,
     dwIoControlCode: DWORD,
     lpvInBuffer: ?*const c_void,
@@ -348,18 +348,18 @@ pub extern "ws2_32" stdcallcc fn WSAIoctl(
     lpcbBytesReturned: LPDWORD,
     lpOverlapped: ?*WSAOVERLAPPED,
     lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
-) c_int;
-pub extern "ws2_32" stdcallcc fn accept(
+) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn accept(
     s: SOCKET,
     addr: ?*sockaddr,
     addrlen: socklen_t,
-) SOCKET;
-pub extern "ws2_32" stdcallcc fn connect(
+) callconv(.Stdcall) SOCKET;
+pub extern "ws2_32" fn connect(
     s: SOCKET,
     name: *const sockaddr,
     namelen: socklen_t,
-) c_int;
-pub extern "ws2_32" stdcallcc fn WSARecv(
+) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSARecv(
     s: SOCKET,
     lpBuffers: [*]const WSABUF,
     dwBufferCount: DWORD,
@@ -367,8 +367,8 @@ pub extern "ws2_32" stdcallcc fn WSARecv(
     lpFlags: *DWORD,
     lpOverlapped: ?*WSAOVERLAPPED,
     lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
-) c_int;
-pub extern "ws2_32" stdcallcc fn WSARecvFrom(
+) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSARecvFrom(
     s: SOCKET,
     lpBuffers: [*]const WSABUF,
     dwBufferCount: DWORD,
@@ -378,8 +378,8 @@ pub extern "ws2_32" stdcallcc fn WSARecvFrom(
     lpFromlen: socklen_t,
     lpOverlapped: ?*WSAOVERLAPPED,
     lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
-) c_int;
-pub extern "ws2_32" stdcallcc fn WSASend(
+) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSASend(
     s: SOCKET,
     lpBuffers: [*]WSABUF,
     dwBufferCount: DWORD,
@@ -387,8 +387,8 @@ pub extern "ws2_32" stdcallcc fn WSASend(
     dwFlags: DWORD,
     lpOverlapped: ?*WSAOVERLAPPED,
     lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
-) c_int;
-pub extern "ws2_32" stdcallcc fn WSASendTo(
+) callconv(.Stdcall) c_int;
+pub extern "ws2_32" fn WSASendTo(
     s: SOCKET,
     lpBuffers: [*]WSABUF,
     dwBufferCount: DWORD,
@@ -398,4 +398,4 @@ pub extern "ws2_32" stdcallcc fn WSASendTo(
     iTolen: socklen_t,
     lpOverlapped: ?*WSAOVERLAPPED,
     lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
-) c_int;
+) callconv(.Stdcall) c_int;
lib/std/os/test.zig
@@ -166,7 +166,7 @@ test "sigaltstack" {
 // analyzed
 const dl_phdr_info = if (@hasDecl(os, "dl_phdr_info")) os.dl_phdr_info else c_void;
 
-export fn iter_fn(info: *dl_phdr_info, size: usize, data: ?*usize) i32 {
+extern fn iter_fn(info: *dl_phdr_info, size: usize, data: ?*usize) i32 {
     if (builtin.os == .windows or builtin.os == .wasi or builtin.os == .macosx)
         return 0;
 
lib/std/special/compiler_rt/arm/aeabi_dcmp.zig
@@ -12,31 +12,31 @@ const ConditionalOperator = enum {
     Gt,
 };
 
-pub nakedcc fn __aeabi_dcmpeq() noreturn {
+pub fn __aeabi_dcmpeq() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Eq});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_dcmplt() noreturn {
+pub fn __aeabi_dcmplt() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Lt});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_dcmple() noreturn {
+pub fn __aeabi_dcmple() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Le});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_dcmpge() noreturn {
+pub fn __aeabi_dcmpge() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Ge});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_dcmpgt() noreturn {
+pub fn __aeabi_dcmpgt() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_dcmp, .{.Gt});
     unreachable;
lib/std/special/compiler_rt/arm/aeabi_fcmp.zig
@@ -12,31 +12,31 @@ const ConditionalOperator = enum {
     Gt,
 };
 
-pub nakedcc fn __aeabi_fcmpeq() noreturn {
+pub fn __aeabi_fcmpeq() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Eq});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_fcmplt() noreturn {
+pub fn __aeabi_fcmplt() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Lt});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_fcmple() noreturn {
+pub fn __aeabi_fcmple() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Le});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_fcmpge() noreturn {
+pub fn __aeabi_fcmpge() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Ge});
     unreachable;
 }
 
-pub nakedcc fn __aeabi_fcmpgt() noreturn {
+pub fn __aeabi_fcmpgt() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, aeabi_fcmp, .{.Gt});
     unreachable;
lib/std/special/compiler_rt/aulldiv.zig
@@ -1,6 +1,6 @@
 const builtin = @import("builtin");
 
-pub extern stdcallcc fn _alldiv(a: i64, b: i64) i64 {
+pub extern fn _alldiv(a: i64, b: i64) callconv(.Stdcall) i64 {
     @setRuntimeSafety(builtin.is_test);
     const s_a = a >> (i64.bit_count - 1);
     const s_b = b >> (i64.bit_count - 1);
@@ -13,7 +13,7 @@ pub extern stdcallcc fn _alldiv(a: i64, b: i64) i64 {
     return (@bitCast(i64, r) ^ s) -% s;
 }
 
-pub nakedcc fn _aulldiv() void {
+pub fn _aulldiv() callconv(.Naked) void {
     @setRuntimeSafety(false);
 
     // The stack layout is:
lib/std/special/compiler_rt/aullrem.zig
@@ -1,6 +1,6 @@
 const builtin = @import("builtin");
 
-pub extern stdcallcc fn _allrem(a: i64, b: i64) i64 {
+pub extern fn _allrem(a: i64, b: i64) callconv(.Stdcall) i64 {
     @setRuntimeSafety(builtin.is_test);
     const s_a = a >> (i64.bit_count - 1);
     const s_b = b >> (i64.bit_count - 1);
@@ -13,7 +13,7 @@ pub extern stdcallcc fn _allrem(a: i64, b: i64) i64 {
     return (@bitCast(i64, r) ^ s) -% s;
 }
 
-pub nakedcc fn _aullrem() void {
+pub fn _aullrem() callconv(.Naked) void {
     @setRuntimeSafety(false);
 
     // The stack layout is:
lib/std/special/compiler_rt/stack_probe.zig
@@ -1,7 +1,7 @@
 const builtin = @import("builtin");
 
 // Zig's own stack-probe routine (available only on x86 and x86_64)
-pub nakedcc fn zig_probe_stack() void {
+pub fn zig_probe_stack() callconv(.Naked) void {
     @setRuntimeSafety(false);
 
     // Versions of the Linux kernel before 5.1 treat any access below SP as
@@ -180,11 +180,11 @@ fn win_probe_stack_adjust_sp() void {
 // ___chkstk (__alloca) | yes    | yes    |
 // ___chkstk_ms         | no     | no     |
 
-pub nakedcc fn _chkstk() void {
+pub fn _chkstk() callconv(.Naked) void {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{});
 }
-pub nakedcc fn __chkstk() void {
+pub fn __chkstk() callconv(.Naked) void {
     @setRuntimeSafety(false);
     switch (builtin.arch) {
         .i386 => @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}),
@@ -192,15 +192,15 @@ pub nakedcc fn __chkstk() void {
         else => unreachable,
     }
 }
-pub nakedcc fn ___chkstk() void {
+pub fn ___chkstk() callconv(.Naked) void {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{});
 }
-pub nakedcc fn __chkstk_ms() void {
+pub fn __chkstk_ms() callconv(.Naked) void {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{});
 }
-pub nakedcc fn ___chkstk_ms() void {
+pub fn ___chkstk_ms() callconv(.Naked) void {
     @setRuntimeSafety(false);
     @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{});
 }
lib/std/special/c.zig
@@ -195,7 +195,7 @@ extern fn __stack_chk_fail() noreturn {
 // TODO we should be able to put this directly in std/linux/x86_64.zig but
 // it causes a segfault in release mode. this is a workaround of calling it
 // across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
-nakedcc fn clone() void {
+fn clone() callconv(.Naked) void {
     switch (builtin.arch) {
         .i386 => {
             // __clone(func, stack, flags, arg, ptid, tls, ctid)
lib/std/special/compiler_rt.zig
@@ -528,7 +528,7 @@ fn usesThumb1PreArmv6(arch: builtin.Arch) bool {
     };
 }
 
-nakedcc fn __aeabi_memcpy() noreturn {
+fn __aeabi_memcpy() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     if (use_thumb_1) {
         asm volatile (
@@ -544,7 +544,7 @@ nakedcc fn __aeabi_memcpy() noreturn {
     unreachable;
 }
 
-nakedcc fn __aeabi_memmove() noreturn {
+fn __aeabi_memmove() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     if (use_thumb_1) {
         asm volatile (
@@ -560,7 +560,7 @@ nakedcc fn __aeabi_memmove() noreturn {
     unreachable;
 }
 
-nakedcc fn __aeabi_memset() noreturn {
+fn __aeabi_memset() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     if (use_thumb_1_pre_armv6) {
         asm volatile (
@@ -591,7 +591,7 @@ nakedcc fn __aeabi_memset() noreturn {
     unreachable;
 }
 
-nakedcc fn __aeabi_memclr() noreturn {
+fn __aeabi_memclr() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     if (use_thumb_1_pre_armv6) {
         asm volatile (
@@ -619,7 +619,7 @@ nakedcc fn __aeabi_memclr() noreturn {
     unreachable;
 }
 
-nakedcc fn __aeabi_memcmp() noreturn {
+fn __aeabi_memcmp() callconv(.Naked) noreturn {
     @setRuntimeSafety(false);
     if (use_thumb_1) {
         asm volatile (
lib/std/zig/ast.zig
@@ -860,6 +860,7 @@ pub const Node = struct {
         lib_name: ?*Node, // populated if this is an extern declaration
         align_expr: ?*Node, // populated if align(A) is present
         section_expr: ?*Node, // populated if linksection(A) is present
+        callconv_expr: ?*Node, // populated if callconv(A) is present
 
         pub const ParamList = SegmentedList(*Node, 2);
 
lib/std/zig/parse.zig
@@ -311,6 +311,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
     const rparen = try expectToken(it, tree, .RParen);
     const align_expr = try parseByteAlign(arena, it, tree);
     const section_expr = try parseLinkSection(arena, it, tree);
+    const callconv_expr = try parseCallconv(arena, it, tree);
     const exclamation_token = eatToken(it, .Bang);
 
     const return_type_expr = (try parseVarType(arena, it, tree)) orelse
@@ -347,6 +348,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
         .lib_name = null,
         .align_expr = align_expr,
         .section_expr = section_expr,
+        .callconv_expr = callconv_expr,
     };
 
     if (cc) |kind| {
@@ -1678,6 +1680,17 @@ fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
     return expr_node;
 }
 
+/// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
+fn parseCallconv(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
+    _ = eatToken(it, .Keyword_callconv) orelse return null;
+    _ = try expectToken(it, tree, .LParen);
+    const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
+        .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
+    });
+    _ = try expectToken(it, tree, .RParen);
+    return expr_node;
+}
+
 /// FnCC
 ///     <- KEYWORD_nakedcc
 ///      / KEYWORD_stdcallcc
lib/std/zig/parser_test.zig
@@ -219,7 +219,7 @@ test "zig fmt: threadlocal" {
 test "zig fmt: linksection" {
     try testCanonical(
         \\export var aoeu: u64 linksection(".text.derp") = 1234;
-        \\export nakedcc fn _start() linksection(".text.boot") noreturn {}
+        \\export fn _start() linksection(".text.boot") callconv(.Naked) noreturn {}
         \\
     );
 }
@@ -2311,7 +2311,7 @@ test "zig fmt: fn type" {
         \\
         \\const a: fn (u8) u8 = undefined;
         \\const b: extern fn (u8) u8 = undefined;
-        \\const c: nakedcc fn (u8) u8 = undefined;
+        \\const c: fn (u8) callconv(.Naked) u8 = undefined;
         \\const ap: fn (u8) u8 = a;
         \\
     );
lib/std/zig/render.zig
@@ -1295,8 +1295,16 @@ fn renderExpression(
                 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space);
             }
 
+            // Some extra machinery is needed to rewrite the old-style cc
+            // notation to the new callconv one
+            var cc_rewrite_str: ?[*:0]const u8 = null;
             if (fn_proto.cc_token) |cc_token| {
-                try renderToken(tree, stream, cc_token, indent, start_col, Space.Space); // stdcallcc
+                var str = tree.tokenSlicePtr(tree.tokens.at(cc_token));
+                if (mem.eql(u8, str, "stdcallcc")) {
+                    cc_rewrite_str = ".Stdcall";
+                } else if (mem.eql(u8, str, "nakedcc")) {
+                    cc_rewrite_str = ".Naked";
+                } else try renderToken(tree, stream, cc_token, indent, start_col, Space.Space); // stdcallcc
             }
 
             const lparen = if (fn_proto.name_token) |name_token| blk: {
@@ -1368,6 +1376,21 @@ fn renderExpression(
                 try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // )
             }
 
+            if (fn_proto.callconv_expr) |callconv_expr| {
+                const section_rparen = tree.nextToken(callconv_expr.lastToken());
+                const section_lparen = tree.prevToken(callconv_expr.firstToken());
+                const section_kw = tree.prevToken(section_lparen);
+
+                try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // section
+                try renderToken(tree, stream, section_lparen, indent, start_col, Space.None); // (
+                try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
+                try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // )
+            } else if (cc_rewrite_str) |str| {
+                try stream.write("callconv(");
+                try stream.write(mem.toSliceConst(u8, str));
+                try stream.write(") ");
+            }
+
             switch (fn_proto.return_type) {
                 ast.Node.FnProto.ReturnType.Explicit => |node| {
                     return renderExpression(allocator, stream, tree, indent, start_col, node, space);
lib/std/zig/tokenizer.zig
@@ -30,6 +30,7 @@ pub const Token = struct {
         Keyword.init("async", .Keyword_async),
         Keyword.init("await", .Keyword_await),
         Keyword.init("break", .Keyword_break),
+        Keyword.init("callconv", .Keyword_callconv),
         Keyword.init("catch", .Keyword_catch),
         Keyword.init("comptime", .Keyword_comptime),
         Keyword.init("const", .Keyword_const),
@@ -162,6 +163,7 @@ pub const Token = struct {
         Keyword_async,
         Keyword_await,
         Keyword_break,
+        Keyword_callconv,
         Keyword_catch,
         Keyword_comptime,
         Keyword_const,
@@ -286,6 +288,7 @@ pub const Token = struct {
                 .Keyword_async => "async",
                 .Keyword_await => "await",
                 .Keyword_break => "break",
+                .Keyword_callconv => "callconv",
                 .Keyword_catch => "catch",
                 .Keyword_comptime => "comptime",
                 .Keyword_const => "const",
lib/std/builtin.zig
@@ -91,6 +91,24 @@ pub const Mode = enum {
     ReleaseSmall,
 };
 
+/// This data structure is used by the Zig language code generation and
+/// therefore must be kept in sync with the compiler implementation.
+pub const CallingConvention = enum {
+    Unspecified,
+    C,
+    Cold,
+    Naked,
+    Async,
+    Interrupt,
+    Signal,
+    Stdcall,
+    Fastcall,
+    Vectorcall,
+    APCS,
+    AAPCS,
+    AAPCSVFP,
+};
+
 pub const TypeId = @TagType(TypeInfo);
 
 /// This data structure is used by the Zig language code generation and
@@ -253,17 +271,6 @@ pub const TypeInfo = union(enum) {
         decls: []Declaration,
     };
 
-    /// This data structure is used by the Zig language code generation and
-    /// therefore must be kept in sync with the compiler implementation.
-    pub const CallingConvention = enum {
-        Unspecified,
-        C,
-        Cold,
-        Naked,
-        Stdcall,
-        Async,
-    };
-
     /// This data structure is used by the Zig language code generation and
     /// therefore must be kept in sync with the compiler implementation.
     pub const FnArg = struct {
@@ -416,7 +423,7 @@ pub const CallOptions = struct {
 /// therefore must be kept in sync with the compiler implementation.
 pub const TestFn = struct {
     name: []const u8,
-    func: fn()anyerror!void,
+    func: fn () anyerror!void,
 };
 
 /// This function type is used by the Zig language code generation and
lib/std/debug.zig
@@ -2475,7 +2475,7 @@ extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *con
     os.abort();
 }
 
-stdcallcc fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) c_long {
+fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(.Stdcall) c_long {
     const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress);
     switch (info.ExceptionRecord.ExceptionCode) {
         windows.EXCEPTION_DATATYPE_MISALIGNMENT => panicExtra(null, exception_address, "Unaligned Memory Access", .{}),
lib/std/fs.zig
@@ -230,7 +230,7 @@ pub const AtomicFile = struct {
             b64_fs_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf);
 
             const file = my_cwd.createFileC(
-                tmp_path_slice, 
+                tmp_path_slice,
                 .{ .mode = mode, .exclusive = true },
             ) catch |err| switch (err) {
                 error.PathAlreadyExists => continue,
lib/std/mutex.zig
@@ -48,8 +48,8 @@ pub const Mutex = if (builtin.single_threaded)
             return self.tryAcquire() orelse @panic("deadlock detected");
         }
     }
-else if (builtin.os == .windows) 
-    // https://locklessinc.com/articles/keyed_events/
+else if (builtin.os == .windows)
+// https://locklessinc.com/articles/keyed_events/
     extern union {
         locked: u8,
         waiters: u32,
@@ -97,8 +97,8 @@ else if (builtin.os == .windows)
                         return Held{ .mutex = self };
                     }
 
-                // otherwise, try and update the waiting count.
-                // then unset the WAKE bit so that another unlocker can wake up a thread.
+                    // otherwise, try and update the waiting count.
+                    // then unset the WAKE bit so that another unlocker can wake up a thread.
                 } else if (@cmpxchgWeak(u32, &self.waiters, waiters, (waiters + WAIT) | 1, .Monotonic, .Monotonic) == null) {
                     const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null);
                     assert(rc == 0);
@@ -118,7 +118,7 @@ else if (builtin.os == .windows)
 
                 while (true) : (SpinLock.loopHint(1)) {
                     const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic);
-                
+
                     // no one is waiting
                     if (waiters < WAIT) return;
                     // someone grabbed the lock and will do the wake instead
@@ -130,14 +130,14 @@ else if (builtin.os == .windows)
                     if (@cmpxchgWeak(u32, &self.mutex.waiters, waiters, waiters - WAIT + WAKE, .Release, .Monotonic) == null) {
                         const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
                         assert(rc == 0);
-                        return;   
+                        return;
                     }
                 }
             }
         };
     }
 else if (builtin.link_libc or builtin.os == .linux)
-    // stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
+// stack-based version of https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
     struct {
         state: usize,
 
@@ -170,8 +170,8 @@ else if (builtin.link_libc or builtin.os == .linux)
 
         pub fn acquire(self: *Mutex) Held {
             return self.tryAcquire() orelse {
-               self.acquireSlow();
-               return Held{ .mutex = self };
+                self.acquireSlow();
+                return Held{ .mutex = self };
             };
         }
 
@@ -237,7 +237,7 @@ else if (builtin.link_libc or builtin.os == .linux)
 
         fn releaseSlow(self: *Mutex) void {
             @setCold(true);
-            
+
             // try and lock the LFIO queue to pop a node off,
             // stopping altogether if its already locked or the queue is empty
             var state = @atomicLoad(usize, &self.state, .Monotonic);
@@ -265,9 +265,10 @@ else if (builtin.link_libc or builtin.os == .linux)
         }
     }
 
-// for platforms without a known OS blocking
-// primitive, default to SpinLock for correctness
-else SpinLock;
+    // for platforms without a known OS blocking
+    // primitive, default to SpinLock for correctness
+else
+    SpinLock;
 
 const TestContext = struct {
     mutex: *Mutex,
lib/std/net.zig
@@ -451,11 +451,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
             .next = null,
         };
         var res: *os.addrinfo = undefined;
-        switch (os.system.getaddrinfo(
-                name_c.ptr,
-                @ptrCast([*:0]const u8, port_c.ptr),
-                &hints,
-                &res)) {
+        switch (os.system.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res)) {
             0 => {},
             c.EAI_ADDRFAMILY => return error.HostLacksNetworkAddresses,
             c.EAI_AGAIN => return error.TemporaryNameServerFailure,
lib/std/reset_event.zig
@@ -14,13 +14,12 @@ const windows = os.windows;
 pub const ResetEvent = struct {
     os_event: OsEvent,
 
-    pub const OsEvent = 
-        if (builtin.single_threaded)
-            DebugEvent
-        else if (builtin.link_libc and builtin.os != .windows and builtin.os != .linux)
-            PosixEvent
-        else
-            AtomicEvent;
+    pub const OsEvent = if (builtin.single_threaded)
+        DebugEvent
+    else if (builtin.link_libc and builtin.os != .windows and builtin.os != .linux)
+        PosixEvent
+    else
+        AtomicEvent;
 
     pub fn init() ResetEvent {
         return ResetEvent{ .os_event = OsEvent.init() };
@@ -105,7 +104,7 @@ const PosixEvent = struct {
     }
 
     fn deinit(self: *PosixEvent) void {
-        // on dragonfly, *destroy() functions can return EINVAL 
+        // on dragonfly, *destroy() functions can return EINVAL
         // for statically initialized pthread structures
         const err = if (builtin.os == .dragonfly) os.EINVAL else 0;
 
@@ -212,8 +211,7 @@ const AtomicEvent = struct {
     fn wait(self: *AtomicEvent, timeout: ?u64) !void {
         var waiters = @atomicLoad(u32, &self.waiters, .Acquire);
         while (waiters != WAKE) {
-            waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire)
-                orelse return Futex.wait(&self.waiters, timeout);
+            waiters = @cmpxchgWeak(u32, &self.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse return Futex.wait(&self.waiters, timeout);
         }
     }
 
@@ -281,7 +279,7 @@ const AtomicEvent = struct {
         pub fn wake(waiters: *u32, wake_count: u32) void {
             const handle = getEventHandle() orelse return SpinFutex.wake(waiters, wake_count);
             const key = @ptrCast(*const c_void, waiters);
-            
+
             var waiting = wake_count;
             while (waiting != 0) : (waiting -= 1) {
                 const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
@@ -408,7 +406,7 @@ test "std.ResetEvent" {
             // wait for receiver to update value and signal output
             self.out.wait();
             testing.expect(self.value == 2);
-            
+
             // update value and signal final input
             self.value = 3;
             self.in.set();
@@ -418,12 +416,12 @@ test "std.ResetEvent" {
             // wait for sender to update value and signal input
             self.in.wait();
             assert(self.value == 1);
-            
+
             // update value and signal output
             self.in.reset();
             self.value = 2;
             self.out.set();
-            
+
             // wait for sender to update value and signal final input
             self.in.wait();
             assert(self.value == 3);
lib/std/spinlock.zig
@@ -60,8 +60,16 @@ pub const SpinLock = struct {
             switch (builtin.arch) {
                 // these instructions use a memory clobber as they
                 // flush the pipeline of any speculated reads/writes.
-                .i386, .x86_64 => asm volatile ("pause" ::: "memory"),
-                .arm, .aarch64 => asm volatile ("yield" ::: "memory"),
+                .i386, .x86_64 => asm volatile ("pause"
+                    :
+                    :
+                    : "memory"
+                ),
+                .arm, .aarch64 => asm volatile ("yield"
+                    :
+                    :
+                    : "memory"
+                ),
                 else => std.os.sched_yield() catch {},
             }
         }
lib/std/start.zig
@@ -43,11 +43,11 @@ comptime {
     }
 }
 
-stdcallcc fn _DllMainCRTStartup(
+fn _DllMainCRTStartup(
     hinstDLL: std.os.windows.HINSTANCE,
     fdwReason: std.os.windows.DWORD,
     lpReserved: std.os.windows.LPVOID,
-) std.os.windows.BOOL {
+) callconv(.Stdcall) std.os.windows.BOOL {
     if (@hasDecl(root, "DllMain")) {
         return root.DllMain(hinstDLL, fdwReason, lpReserved);
     }
@@ -84,7 +84,7 @@ extern fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) u
     }
 }
 
-nakedcc fn _start() noreturn {
+fn _start() callconv(.Naked) noreturn {
     if (builtin.os == builtin.Os.wasi) {
         // This is marked inline because for some reason LLVM in release mode fails to inline it,
         // and we want fewer call frames in stack traces.
@@ -127,7 +127,7 @@ nakedcc fn _start() noreturn {
     @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
 }
 
-stdcallcc fn WinMainCRTStartup() noreturn {
+fn WinMainCRTStartup() callconv(.Stdcall) noreturn {
     @setAlignStack(16);
     if (!builtin.single_threaded) {
         _ = @import("start_windows_tls.zig");
src/all_types.hpp
@@ -57,6 +57,22 @@ enum PtrLen {
     PtrLenC,
 };
 
+enum CallingConvention {
+    CallingConventionUnspecified,
+    CallingConventionC,
+    CallingConventionCold,
+    CallingConventionNaked,
+    CallingConventionAsync,
+    CallingConventionInterrupt,
+    CallingConventionSignal,
+    CallingConventionStdcall,
+    CallingConventionFastcall,
+    CallingConventionVectorcall,
+    CallingConventionAPCS,
+    CallingConventionAAPCS,
+    CallingConventionAAPCSVFP,
+};
+
 // This one corresponds to the builtin.zig enum.
 enum BuiltinPtrSize {
     BuiltinPtrSizeOne,
@@ -398,6 +414,7 @@ struct LazyValueFnType {
     IrInstruction *align_inst; // can be null
     IrInstruction *return_type;
 
+    CallingConvention cc;
     bool is_generic;
 };
 
@@ -612,15 +629,6 @@ enum NodeType {
     NodeTypeVarFieldType,
 };
 
-enum CallingConvention {
-    CallingConventionUnspecified,
-    CallingConventionC,
-    CallingConventionCold,
-    CallingConventionNaked,
-    CallingConventionStdcall,
-    CallingConventionAsync,
-};
-
 enum FnInline {
     FnInlineAuto,
     FnInlineAlways,
@@ -639,10 +647,14 @@ struct AstNodeFnProto {
     AstNode *align_expr;
     // populated if the "section(S)" is present
     AstNode *section_expr;
+    // populated if the "callconv(S)" is present
+    AstNode *callconv_expr;
     Buf doc_comments;
 
     FnInline fn_inline;
-    CallingConvention cc;
+    bool is_nakedcc;
+    bool is_stdcallcc;
+    bool is_async;
 
     VisibMod visib_mod;
     bool auto_err_set;
@@ -1597,6 +1609,7 @@ struct ZigFn {
     Buf **param_names;
     IrInstruction *err_code_spill;
     AstNode *assumed_non_async;
+    CallingConvention cc;
 
     AstNode *fn_no_inline_set_node;
     AstNode *fn_static_eval_set_node;
@@ -3549,6 +3562,7 @@ struct IrInstructionFnProto {
 
     IrInstruction **param_types;
     IrInstruction *align_value;
+    IrInstruction *callconv_value;
     IrInstruction *return_type;
     bool is_var_args;
 };
src/analyze.cpp
@@ -919,24 +919,19 @@ ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) {
 
 const char *calling_convention_name(CallingConvention cc) {
     switch (cc) {
-        case CallingConventionUnspecified: return "undefined";
-        case CallingConventionC: return "ccc";
-        case CallingConventionCold: return "coldcc";
-        case CallingConventionNaked: return "nakedcc";
-        case CallingConventionStdcall: return "stdcallcc";
-        case CallingConventionAsync: return "async";
-    }
-    zig_unreachable();
-}
-
-static const char *calling_convention_fn_type_str(CallingConvention cc) {
-    switch (cc) {
-        case CallingConventionUnspecified: return "";
-        case CallingConventionC: return "extern ";
-        case CallingConventionCold: return "coldcc ";
-        case CallingConventionNaked: return "nakedcc ";
-        case CallingConventionStdcall: return "stdcallcc ";
-        case CallingConventionAsync: return "async ";
+        case CallingConventionUnspecified: return "Unspecified";
+        case CallingConventionC: return "C";
+        case CallingConventionCold: return "Cold";
+        case CallingConventionNaked: return "Naked";
+        case CallingConventionAsync: return "Async";
+        case CallingConventionInterrupt: return "Interrupt";
+        case CallingConventionSignal: return "Signal";
+        case CallingConventionStdcall: return "Stdcall";
+        case CallingConventionFastcall: return "Fastcall";
+        case CallingConventionVectorcall: return "Vectorcall";
+        case CallingConventionAPCS: return "Apcs";
+        case CallingConventionAAPCS: return "Aapcs";
+        case CallingConventionAAPCSVFP: return "Aapcsvfp";
     }
     zig_unreachable();
 }
@@ -949,7 +944,14 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
         case CallingConventionC:
         case CallingConventionCold:
         case CallingConventionNaked:
+        case CallingConventionInterrupt:
+        case CallingConventionSignal:
         case CallingConventionStdcall:
+        case CallingConventionFastcall:
+        case CallingConventionVectorcall:
+        case CallingConventionAPCS:
+        case CallingConventionAAPCS:
+        case CallingConventionAAPCSVFP:
             return false;
     }
     zig_unreachable();
@@ -1006,8 +1008,8 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
 
     // populate the name of the type
     buf_resize(&fn_type->name, 0);
-    const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
-    buf_appendf(&fn_type->name, "%s", cc_str);
+    if (fn_type->data.fn.fn_type_id.cc == CallingConventionC)
+        buf_append_str(&fn_type->name, "extern ");
     buf_appendf(&fn_type->name, "fn(");
     for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
         FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
@@ -1026,6 +1028,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     if (fn_type_id->alignment != 0) {
         buf_appendf(&fn_type->name, " align(%" PRIu32 ")", fn_type_id->alignment);
     }
+    if (fn_type_id->cc != CallingConventionUnspecified && fn_type_id->cc != CallingConventionC) {
+        buf_appendf(&fn_type->name, " callconv(%s)", calling_convention_name(fn_type_id->cc));
+    }
     buf_appendf(&fn_type->name, " %s", buf_ptr(&fn_type_id->return_type->name));
 
     // The fn_type is a pointer; not to be confused with the raw function type.
@@ -1442,8 +1447,8 @@ ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
 ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     ZigType *fn_type = new_type_table_entry(ZigTypeIdFn);
     buf_resize(&fn_type->name, 0);
-    const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
-    buf_appendf(&fn_type->name, "%s", cc_str);
+    if (fn_type->data.fn.fn_type_id.cc == CallingConventionC)
+        buf_append_str(&fn_type->name, "extern ");
     buf_appendf(&fn_type->name, "fn(");
     size_t i = 0;
     for (; i < fn_type_id->next_param_index; i += 1) {
@@ -1455,7 +1460,11 @@ ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
         const char *comma_str = (i == 0) ? "" : ",";
         buf_appendf(&fn_type->name, "%svar", comma_str);
     }
-    buf_appendf(&fn_type->name, ")var");
+    buf_append_str(&fn_type->name, ")");
+    if (fn_type_id->cc != CallingConventionUnspecified && fn_type_id->cc != CallingConventionC) {
+        buf_appendf(&fn_type->name, " callconv(%s)", calling_convention_name(fn_type_id->cc));
+    }
+    buf_append_str(&fn_type->name, " var");
 
     fn_type->data.fn.fn_type_id = *fn_type_id;
     fn_type->data.fn.is_generic = true;
@@ -1465,17 +1474,25 @@ ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     return fn_type;
 }
 
-void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc) {
+CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto) {
+    if (fn_proto->is_nakedcc)
+        return CallingConventionNaked;
+    if (fn_proto->is_stdcallcc)
+        return CallingConventionStdcall;
+    if (fn_proto->is_async)
+        return CallingConventionAsync;
+    // Compatible with the C ABI
+    if (fn_proto->is_extern || fn_proto->is_export)
+        return CallingConventionC;
+
+    return CallingConventionUnspecified;
+}
+
+void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc) {
     assert(proto_node->type == NodeTypeFnProto);
     AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
-    if (fn_proto->cc == CallingConventionUnspecified) {
-        bool extern_abi = fn_proto->is_extern || fn_proto->is_export;
-        fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified;
-    } else {
-        fn_type_id->cc = fn_proto->cc;
-    }
-
+    fn_type_id->cc = cc;
     fn_type_id->param_count = fn_proto->params.length;
     fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc);
     fn_type_id->next_param_index = 0;
@@ -1690,7 +1707,8 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
             return type_allowed_in_extern(g, type_entry->data.array.child_type, result);
         case ZigTypeIdFn:
             *result = type_entry->data.fn.fn_type_id.cc == CallingConventionC ||
-                 type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall;
+                 type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall ||
+                 type_entry->data.fn.fn_type_id.cc == CallingConventionAAPCS;
             return ErrorNone;
         case ZigTypeIdPointer:
             if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
@@ -1750,7 +1768,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
     Error err;
 
     FnTypeId fn_type_id = {0};
-    init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
+    init_fn_type_id(&fn_type_id, proto_node, fn_entry->cc, proto_node->data.fn_proto.params.length);
 
     for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
         AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index);
@@ -3393,27 +3411,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
             get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, false);
         }
 
-        if (fn_proto->is_export) {
-            switch (fn_proto->cc) {
-                case CallingConventionAsync: {
-                    add_node_error(g, fn_def_node,
-                        buf_sprintf("exported function cannot be async"));
-                } break;
-                case CallingConventionC:
-                case CallingConventionNaked:
-                case CallingConventionCold:
-                case CallingConventionStdcall:
-                case CallingConventionUnspecified:
-                    // An exported function without a specific calling
-                    // convention defaults to C
-                    CallingConvention cc = (fn_proto->cc != CallingConventionUnspecified) ?
-                        fn_proto->cc : CallingConventionC;
-                    add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
-                                  GlobalLinkageIdStrong, cc);
-                    break;
-            }
-        }
-
         if (!is_extern) {
             fn_table_entry->fndef_scope = create_fndef_scope(g,
                 fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry);
@@ -3432,6 +3429,21 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
 
         Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
 
+        fn_table_entry->cc = cc_from_fn_proto(fn_proto);
+        if (fn_proto->callconv_expr != nullptr) {
+            ZigType *cc_enum_value = get_builtin_type(g, "CallingConvention");
+
+            ZigValue *result_val = analyze_const_value(g, child_scope, fn_proto->callconv_expr,
+                cc_enum_value, nullptr, UndefBad);
+            if (type_is_invalid(result_val->type)) {
+                fn_table_entry->type_entry = g->builtin_types.entry_invalid;
+                tld_fn->base.resolution = TldResolutionInvalid;
+                return;
+            }
+
+            fn_table_entry->cc = (CallingConvention)bigint_as_u32(&result_val->data.x_enum_tag);
+        }
+
         fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry);
 
         if (fn_proto->section_expr != nullptr) {
@@ -3450,10 +3462,42 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
                 g->fn_defs.append(fn_table_entry);
         }
 
+        const CallingConvention fn_cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc;
+
+        if (fn_proto->is_export) {
+            switch (fn_cc) {
+                case CallingConventionAsync:
+                    add_node_error(g, fn_def_node,
+                        buf_sprintf("exported function cannot be async"));
+                    tld_fn->base.resolution = TldResolutionInvalid;
+                    return;
+                case CallingConventionC:
+                case CallingConventionCold:
+                case CallingConventionNaked:
+                case CallingConventionInterrupt:
+                case CallingConventionSignal:
+                case CallingConventionStdcall:
+                case CallingConventionFastcall:
+                case CallingConventionVectorcall:
+                case CallingConventionAPCS:
+                case CallingConventionAAPCS:
+                case CallingConventionAAPCSVFP:
+                    add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
+                                  GlobalLinkageIdStrong, fn_cc);
+                    break;
+                case CallingConventionUnspecified:
+                    // An exported function without a specific calling
+                    // convention defaults to C
+                    add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
+                                  GlobalLinkageIdStrong, CallingConventionC);
+                    break;
+            }
+        }
+
         // if the calling convention implies that it cannot be async, we save that for later
         // and leave the value to be nullptr to indicate that we have not emitted possible
         // compile errors for improperly calling async functions.
-        if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
+        if (fn_cc == CallingConventionAsync) {
             fn_table_entry->inferred_async_node = fn_table_entry->proto_node;
         }
     } else if (source_node->type == NodeTypeTestDecl) {
src/analyze.hpp
@@ -100,7 +100,7 @@ ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node);
 void append_namespace_qualification(CodeGen *g, Buf *buf, ZigType *container_type);
 ZigFn *create_fn(CodeGen *g, AstNode *proto_node);
 ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value);
-void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_count_alloc);
+void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, CallingConvention cc, size_t param_count_alloc);
 AstNode *get_param_decl_node(ZigFn *fn_entry, size_t index);
 Error ATTRIBUTE_MUST_USE type_resolve(CodeGen *g, ZigType *type_entry, ResolveStatus status);
 void complete_enum(CodeGen *g, ZigType *enum_type);
@@ -259,6 +259,7 @@ ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *
 
 void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn);
 bool fn_is_async(ZigFn *fn);
+CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto);
 
 Error type_val_resolve_abi_align(CodeGen *g, ZigValue *type_val, uint32_t *abi_align);
 Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ZigValue *type_val,
src/ast_render.cpp
@@ -488,6 +488,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                     render_node_grouped(ar, node->data.fn_proto.section_expr);
                     fprintf(ar->f, ")");
                 }
+                if (node->data.fn_proto.callconv_expr) {
+                    fprintf(ar->f, " callconv(");
+                    render_node_grouped(ar, node->data.fn_proto.callconv_expr);
+                    fprintf(ar->f, ")");
+                }
 
                 if (node->data.fn_proto.return_var_token != nullptr) {
                     fprintf(ar->f, "var");
src/codegen.cpp
@@ -284,14 +284,43 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
         case CallingConventionNaked:
             zig_unreachable();
         case CallingConventionStdcall:
-            // stdcall calling convention only works on x86.
-            if (g->zig_target->arch == ZigLLVM_x86) {
+            if (g->zig_target->arch == ZigLLVM_x86)
                 return LLVMX86StdcallCallConv;
-            } else {
-                return LLVMCCallConv;
-            }
+            return LLVMCCallConv;
+        case CallingConventionFastcall:
+            if (g->zig_target->arch == ZigLLVM_x86)
+                return LLVMX86FastcallCallConv;
+            return LLVMFastCallConv;
+        case CallingConventionVectorcall:
+            if (g->zig_target->arch == ZigLLVM_x86)
+                return LLVMX86VectorCallCallConv;
+            return LLVMCCallConv;
         case CallingConventionAsync:
             return LLVMFastCallConv;
+        case CallingConventionAPCS:
+            if (target_is_arm(g->zig_target))
+                return LLVMARMAPCSCallConv;
+            return LLVMCCallConv;
+        case CallingConventionAAPCS:
+            if (target_is_arm(g->zig_target))
+                return LLVMARMAAPCSCallConv;
+            return LLVMCCallConv;
+        case CallingConventionAAPCSVFP:
+            if (target_is_arm(g->zig_target))
+                return LLVMARMAAPCSVFPCallConv;
+            return LLVMCCallConv;
+        case CallingConventionInterrupt:
+            if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64)
+                return LLVMX86INTRCallConv;
+            if (g->zig_target->arch == ZigLLVM_avr)
+                return LLVMAVRINTRCallConv;
+            if (g->zig_target->arch == ZigLLVM_msp430)
+                return LLVMMSP430INTRCallConv;
+            return LLVMCCallConv;
+        case CallingConventionSignal:
+            if (g->zig_target->arch == ZigLLVM_avr)
+                return LLVMAVRSIGNALCallConv;
+            return LLVMCCallConv;
     }
     zig_unreachable();
 }
@@ -383,7 +412,14 @@ static bool cc_want_sret_attr(CallingConvention cc) {
             zig_unreachable();
         case CallingConventionC:
         case CallingConventionCold:
+        case CallingConventionInterrupt:
+        case CallingConventionSignal:
         case CallingConventionStdcall:
+        case CallingConventionFastcall:
+        case CallingConventionVectorcall:
+        case CallingConventionAPCS:
+        case CallingConventionAAPCS:
+        case CallingConventionAAPCSVFP:
             return true;
         case CallingConventionAsync:
         case CallingConventionUnspecified:
@@ -8463,8 +8499,15 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
     static_assert(CallingConventionC == 1, "");
     static_assert(CallingConventionCold == 2, "");
     static_assert(CallingConventionNaked == 3, "");
-    static_assert(CallingConventionStdcall == 4, "");
-    static_assert(CallingConventionAsync == 5, "");
+    static_assert(CallingConventionAsync == 4, "");
+    static_assert(CallingConventionInterrupt == 5, "");
+    static_assert(CallingConventionSignal == 6, "");
+    static_assert(CallingConventionStdcall == 7, "");
+    static_assert(CallingConventionFastcall == 8, "");
+    static_assert(CallingConventionVectorcall == 9, "");
+    static_assert(CallingConventionAPCS == 10, "");
+    static_assert(CallingConventionAAPCS == 11, "");
+    static_assert(CallingConventionAAPCSVFP == 12, "");
 
     static_assert(FnInlineAuto == 0, "");
     static_assert(FnInlineAlways == 1, "");
src/ir.cpp
@@ -3250,12 +3250,13 @@ static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope,
 }
 
 static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
-    IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type,
-    bool is_var_args)
+    IrInstruction **param_types, IrInstruction *align_value, IrInstruction *callconv_value,
+    IrInstruction *return_type, bool is_var_args)
 {
     IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
     instruction->param_types = param_types;
     instruction->align_value = align_value;
+    instruction->callconv_value = callconv_value;
     instruction->return_type = return_type;
     instruction->is_var_args = is_var_args;
 
@@ -3266,6 +3267,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s
         if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
     }
     if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
+    if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block);
     ir_ref_instruction(return_type, irb->current_basic_block);
 
     return &instruction->base;
@@ -8850,6 +8852,13 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
             return irb->codegen->invalid_instruction;
     }
 
+    IrInstruction *callconv_value = nullptr;
+    if (node->data.fn_proto.callconv_expr != nullptr) {
+        callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope);
+        if (callconv_value == irb->codegen->invalid_instruction)
+            return irb->codegen->invalid_instruction;
+    }
+
     IrInstruction *return_type;
     if (node->data.fn_proto.return_var_token == nullptr) {
         if (node->data.fn_proto.return_type == nullptr) {
@@ -8866,7 +8875,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
         //return_type = nullptr;
     }
 
-    return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args);
+    return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args);
 }
 
 static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
@@ -16736,9 +16745,16 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
                     add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
                 } break;
                 case CallingConventionC:
-                case CallingConventionNaked:
                 case CallingConventionCold:
+                case CallingConventionNaked:
+                case CallingConventionInterrupt:
+                case CallingConventionSignal:
                 case CallingConventionStdcall:
+                case CallingConventionFastcall:
+                case CallingConventionVectorcall:
+                case CallingConventionAPCS:
+                case CallingConventionAAPCS:
+                case CallingConventionAAPCSVFP:
                     add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);
                     break;
             }
@@ -18101,7 +18117,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstruction *source_i
         return ira->codegen->invalid_instruction;
     }
 
-
     if (fn_type_id->is_var_args) {
         if (call_param_count < src_param_count) {
             ErrorMsg *msg = ir_add_error_node(ira, source_node,
@@ -18254,8 +18269,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstruction *source_i
         buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name);
         impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_node, parent_scope, impl_fn);
         impl_fn->child_scope = &impl_fn->fndef_scope->base;
+        impl_fn->cc = fn_entry->cc;
         FnTypeId inst_fn_type_id = {0};
-        init_fn_type_id(&inst_fn_type_id, fn_proto_node, new_fn_arg_count);
+        init_fn_type_id(&inst_fn_type_id, fn_proto_node, fn_type_id->cc, new_fn_arg_count);
         inst_fn_type_id.param_count = 0;
         inst_fn_type_id.is_var_args = false;
 
@@ -22592,8 +22608,8 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr
                     // calling_convention: TypeInfo.CallingConvention
                     ensure_field_index(fn_decl_val->type, "calling_convention", 2);
                     fn_decl_fields[2]->special = ConstValSpecialStatic;
-                    fn_decl_fields[2]->type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
-                    bigint_init_unsigned(&fn_decl_fields[2]->data.x_enum_tag, fn_node->cc);
+                    fn_decl_fields[2]->type = get_builtin_type(ira->codegen, "CallingConvention");
+                    bigint_init_unsigned(&fn_decl_fields[2]->data.x_enum_tag, fn_entry->cc);
                     // is_var_args: bool
                     ensure_field_index(fn_decl_val->type, "is_var_args", 3);
                     bool is_varargs = fn_node->is_var_args;
@@ -23280,7 +23296,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
                 // calling_convention: TypeInfo.CallingConvention
                 ensure_field_index(result->type, "calling_convention", 0);
                 fields[0]->special = ConstValSpecialStatic;
-                fields[0]->type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
+                fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
                 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
                 // is_generic: bool
                 ensure_field_index(result->type, "is_generic", 1);
@@ -26192,6 +26208,21 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct
         return ira->codegen->invalid_instruction;
     }
 
+    lazy_fn_type->cc = cc_from_fn_proto(&proto_node->data.fn_proto);
+    if (instruction->callconv_value != nullptr) {
+        ZigType *cc_enum_type = get_builtin_type(ira->codegen, "CallingConvention");
+
+        IrInstruction *casted_value = ir_implicit_cast(ira, instruction->callconv_value, cc_enum_type);
+        if (type_is_invalid(casted_value->value->type))
+            return ira->codegen->invalid_instruction;
+
+        ZigValue *const_value = ir_resolve_const(ira, casted_value, UndefBad);
+        if (const_value == nullptr)
+            return ira->codegen->invalid_instruction;
+
+        lazy_fn_type->cc = (CallingConvention)bigint_as_u32(&const_value->data.x_enum_tag);
+    }
+
     size_t param_count = proto_node->data.fn_proto.params.length;
     lazy_fn_type->proto_node = proto_node;
     lazy_fn_type->param_types = allocate<IrInstruction *>(param_count);
@@ -26202,9 +26233,11 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct
 
         bool param_is_var_args = param_node->data.param_decl.is_var_args;
         if (param_is_var_args) {
-            if (proto_node->data.fn_proto.cc == CallingConventionC) {
+            const CallingConvention cc = lazy_fn_type->cc;
+
+            if (cc == CallingConventionC) {
                 break;
-            } else if (proto_node->data.fn_proto.cc == CallingConventionUnspecified) {
+            } else if (cc == CallingConventionUnspecified) {
                 lazy_fn_type->is_generic = true;
                 return result;
             } else {
@@ -29062,7 +29095,7 @@ static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, La
     AstNode *proto_node = lazy_fn_type->proto_node;
 
     FnTypeId fn_type_id = {0};
-    init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
+    init_fn_type_id(&fn_type_id, proto_node, lazy_fn_type->cc, proto_node->data.fn_proto.params.length);
 
     for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
         AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
src/parser.cpp
@@ -92,6 +92,7 @@ static Token *ast_parse_block_label(ParseContext *pc);
 static AstNode *ast_parse_field_init(ParseContext *pc);
 static AstNode *ast_parse_while_continue_expr(ParseContext *pc);
 static AstNode *ast_parse_link_section(ParseContext *pc);
+static AstNode *ast_parse_callconv(ParseContext *pc);
 static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc);
 static AstNode *ast_parse_param_decl(ParseContext *pc);
 static AstNode *ast_parse_param_type(ParseContext *pc);
@@ -676,7 +677,9 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
             fn_proto->column = first->start_column;
             fn_proto->data.fn_proto.visib_mod = visib_mod;
             fn_proto->data.fn_proto.doc_comments = *doc_comments;
-            fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
+            // ast_parse_fn_cc may set it
+            if (!fn_proto->data.fn_proto.is_extern)
+                fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
             fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
             switch (first->id) {
                 case TokenIdKeywordInline:
@@ -761,7 +764,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
         // The extern keyword for fn CC is also used for container decls.
         // We therefore put it back, as allow container decl to consume it
         // later.
-        if (fn_cc.cc == CallingConventionC) {
+        if (fn_cc.is_extern) {
             fn = eat_token_if(pc, TokenIdKeywordFn);
             if (fn == nullptr) {
                 put_back_token(pc);
@@ -784,6 +787,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
 
     AstNode *align_expr = ast_parse_byte_align(pc);
     AstNode *section_expr = ast_parse_link_section(pc);
+    AstNode *callconv_expr = ast_parse_callconv(pc);
     Token *var = eat_token_if(pc, TokenIdKeywordVar);
     Token *exmark = nullptr;
     AstNode *return_type = nullptr;
@@ -798,6 +802,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
     res->data.fn_proto.params = params;
     res->data.fn_proto.align_expr = align_expr;
     res->data.fn_proto.section_expr = section_expr;
+    res->data.fn_proto.callconv_expr = callconv_expr;
     res->data.fn_proto.return_var_token = var;
     res->data.fn_proto.auto_err_set = exmark != nullptr;
     res->data.fn_proto.return_type = return_type;
@@ -2099,6 +2104,18 @@ static AstNode *ast_parse_link_section(ParseContext *pc) {
     return res;
 }
 
+// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
+static AstNode *ast_parse_callconv(ParseContext *pc) {
+    Token *first = eat_token_if(pc, TokenIdKeywordCallconv);
+    if (first == nullptr)
+        return nullptr;
+
+    expect_token(pc, TokenIdLParen);
+    AstNode *res = ast_expect(pc, ast_parse_expr);
+    expect_token(pc, TokenIdRParen);
+    return res;
+}
+
 // FnCC
 //     <- KEYWORD_nakedcc
 //      / KEYWORD_stdcallcc
@@ -2107,19 +2124,19 @@ static AstNode *ast_parse_link_section(ParseContext *pc) {
 static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc) {
     AstNodeFnProto res = {};
     if (eat_token_if(pc, TokenIdKeywordNakedCC) != nullptr) {
-        res.cc = CallingConventionNaked;
+        res.is_nakedcc = true;
         return Optional<AstNodeFnProto>::some(res);
     }
     if (eat_token_if(pc, TokenIdKeywordStdcallCC) != nullptr) {
-        res.cc = CallingConventionStdcall;
+        res.is_stdcallcc = true;
         return Optional<AstNodeFnProto>::some(res);
     }
-    if (eat_token_if(pc, TokenIdKeywordExtern) != nullptr) {
-        res.cc = CallingConventionC;
+    if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) {
+        res.is_async = true;
         return Optional<AstNodeFnProto>::some(res);
     }
-    if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) {
-        res.cc = CallingConventionAsync;
+    if (eat_token_if(pc, TokenIdKeywordExtern) != nullptr) {
+        res.is_extern = true;
         return Optional<AstNodeFnProto>::some(res);
     }
 
src/tokenizer.cpp
@@ -110,6 +110,7 @@ static const struct ZigKeyword zig_keywords[] = {
     {"async", TokenIdKeywordAsync},
     {"await", TokenIdKeywordAwait},
     {"break", TokenIdKeywordBreak},
+    {"callconv", TokenIdKeywordCallconv},
     {"catch", TokenIdKeywordCatch},
     {"comptime", TokenIdKeywordCompTime},
     {"const", TokenIdKeywordConst},
@@ -1545,6 +1546,7 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordAsm: return "asm";
         case TokenIdKeywordBreak: return "break";
         case TokenIdKeywordCatch: return "catch";
+        case TokenIdKeywordCallconv: return "callconv";
         case TokenIdKeywordCompTime: return "comptime";
         case TokenIdKeywordConst: return "const";
         case TokenIdKeywordContinue: return "continue";
src/tokenizer.hpp
@@ -59,6 +59,7 @@ enum TokenId {
     TokenIdKeywordAwait,
     TokenIdKeywordBreak,
     TokenIdKeywordCatch,
+    TokenIdKeywordCallconv,
     TokenIdKeywordCompTime,
     TokenIdKeywordConst,
     TokenIdKeywordContinue,
src-self-hosted/translate_c.zig
@@ -11,7 +11,7 @@ const CToken = ctok.CToken;
 const mem = std.mem;
 const math = std.math;
 
-const CallingConvention = std.builtin.TypeInfo.CallingConvention;
+const CallingConvention = std.builtin.CallingConvention;
 
 pub const ClangErrMsg = Stage2ErrorMsg;
 
@@ -3529,6 +3529,7 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
         .lib_name = null,
         .align_expr = null,
         .section_expr = null,
+        .callconv_expr = null,
     };
 
     const block = try transCreateNodeBlock(c, null);
@@ -4135,6 +4136,7 @@ fn finishTransFnProto(
         .lib_name = null,
         .align_expr = null,
         .section_expr = null,
+        .callconv_expr = null,
     };
     return fn_proto;
 }
@@ -4483,6 +4485,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u
         .lib_name = null,
         .align_expr = null,
         .section_expr = null,
+        .callconv_expr = null,
     };
 
     const block = try transCreateNodeBlock(c, null);
src-self-hosted/type.zig
@@ -337,7 +337,7 @@ pub const Type = struct {
             }
         };
 
-        const CallingConvention = builtin.TypeInfo.CallingConvention;
+        const CallingConvention = builtin.CallingConvention;
 
         pub const Param = struct {
             is_noalias: bool,
@@ -352,6 +352,7 @@ pub const Type = struct {
                 .Naked => "nakedcc ",
                 .Stdcall => "stdcallcc ",
                 .Async => "async ",
+                else => unreachable,
             };
         }
 
test/stage1/behavior/type_info.zig
@@ -202,7 +202,7 @@ fn testUnion() void {
     expect(typeinfo_info.Union.fields[4].enum_field != null);
     expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
     expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
-    expect(typeinfo_info.Union.decls.len == 21);
+    expect(typeinfo_info.Union.decls.len == 20);
 
     const TestNoTagUnion = union {
         Foo: void,
@@ -266,7 +266,7 @@ test "type info: function type info" {
 fn testFunction() void {
     const fn_info = @typeInfo(@TypeOf(foo));
     expect(@as(TypeId, fn_info) == TypeId.Fn);
-    expect(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified);
+    expect(fn_info.Fn.calling_convention == .Unspecified);
     expect(fn_info.Fn.is_generic);
     expect(fn_info.Fn.args.len == 2);
     expect(fn_info.Fn.is_var_args);
test/compile_errors.zig
@@ -752,7 +752,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = @frame();
         \\}
     , &[_][]const u8{
-        "tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async",
+        "tmp.zig:1:1: error: function with calling convention 'C' cannot be async",
         "tmp.zig:5:9: note: @frame() causes function to be async",
     });
 
@@ -765,7 +765,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    suspend;
         \\}
     , &[_][]const u8{
-        "tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async",
+        "tmp.zig:1:1: error: function with calling convention 'C' cannot be async",
         "tmp.zig:3:18: note: await here is a suspend point",
     });
 
@@ -843,7 +843,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    suspend;
         \\}
     , &[_][]const u8{
-        "tmp.zig:1:1: error: function with calling convention 'ccc' cannot be async",
+        "tmp.zig:1:1: error: function with calling convention 'C' cannot be async",
         "tmp.zig:2:8: note: async function call here",
         "tmp.zig:5:8: note: async function call here",
         "tmp.zig:8:5: note: suspends here",
@@ -1140,7 +1140,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    while (true) {}
         \\}
     , &[_][]const u8{
-        "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,var)var'",
+        "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,var) var'",
         "note: only one of the functions is generic",
     });
 
@@ -1362,7 +1362,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    return 0;
         \\}
     , &[_][]const u8{
-        "tmp.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'ccc'",
+        "tmp.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'C'",
     });
 
     cases.add("C pointer to c_void",
@@ -2187,7 +2187,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    f(g);
         \\}
     , &[_][]const u8{
-        "tmp.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime",
+        "tmp.zig:1:9: error: parameter of type 'fn(var) var' must be declared comptime",
     });
 
     cases.add("optional pointer to void in extern struct",
@@ -2859,7 +2859,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\const Foo = enum { A, B, C };
         \\export fn entry(foo: Foo) void { }
     , &[_][]const u8{
-        "tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'",
+        "tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'",
     });
 
     cases.add("function with non-extern non-packed struct parameter",
@@ -2870,7 +2870,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\};
         \\export fn entry(foo: Foo) void { }
     , &[_][]const u8{
-        "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'",
+        "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'",
     });
 
     cases.add("function with non-extern non-packed union parameter",
@@ -2881,7 +2881,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\};
         \\export fn entry(foo: Foo) void { }
     , &[_][]const u8{
-        "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'",
+        "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'",
     });
 
     cases.add("switch on enum with 1 field with no prongs",
@@ -2977,8 +2977,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    bar(&{});
         \\}
     , &[_][]const u8{
-        "tmp.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'",
-        "tmp.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'",
+        "tmp.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C'",
+        "tmp.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C'",
     });
 
     cases.add("implicit semicolon - block statement",
@@ -4552,7 +4552,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    return x + y;
         \\}
     , &[_][]const u8{
-        "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'",
+        "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C'",
     });
 
     cases.add("extern function with comptime parameter",
@@ -4562,7 +4562,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\}
         \\export fn entry() usize { return @sizeOf(@TypeOf(f)); }
     , &[_][]const u8{
-        "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'",
+        "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C'",
     });
 
     cases.add("convert fixed size array to slice with invalid size",
@@ -6303,7 +6303,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = @TypeOf(generic).ReturnType;
         \\}
     , &[_][]const u8{
-        "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic",
+        "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(var) var' is generic",
     });
 
     cases.add("getting @ArgType of generic function",
@@ -6312,7 +6312,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         \\    _ = @ArgType(@TypeOf(generic), 0);
         \\}
     , &[_][]const u8{
-        "tmp.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",
+        "tmp.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var) var' is generic",
     });
 
     cases.add("unsupported modifier at start of asm output constraint",