{"id":5056,"date":"2026-11-05T15:00:58","date_gmt":"2026-11-05T23:00:58","guid":{"rendered":"https:\/\/www.flamingspork.com\/blog\/?p=5056"},"modified":"2026-11-05T15:00:58","modified_gmt":"2026-11-05T23:00:58","slug":"zig-self-hosted-compiler-cant-compile-a-16-byte-atomicload","status":"publish","type":"post","link":"https:\/\/www.flamingspork.com\/blog\/2026\/11\/05\/zig-self-hosted-compiler-cant-compile-a-16-byte-atomicload\/","title":{"rendered":"Zig self-hosted compiler can&#8217;t compile a 16-byte @atomicLoad"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Previously, I wrote about how the <a href=\"https:\/\/www.flamingspork.com\/blog\/2026\/10\/26\/zig-self-hosted-backend-lowers-a-seq_cst-atomicrmw-xchg-with-a-discarded-result-to-a-plain-mov\/\">Zig self-hosted backend lowers a seq_cst @atomicRmw(.Xchg) with a discarded result to a plain mov<\/a>, but this isn&#8217;t the only issue with the self-hosted backend and atomics.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A valid bit of Zig code is <code>@atomicLoad(u128, p, .seq_cst)<\/code> &#8211; an atomic load of an unsigned 128 bit value.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A trivial example in Zig:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export fn load128(p: *u128) u128 {\n    return @atomicLoad(u128, p, .seq_cst);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The equivalent trivial example in C:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unsigned __int128 load128(unsigned __int128 *p) { return __atomic_load_n(p, __ATOMIC_SEQ_CST); }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When building for a target that has the <code>cmpxchg16b<\/code> instruction (such as the x86-64v2 target, the base x86-64 processor level for several Linux distributions, or by specifying <code>-mcx16<\/code> to enable just that instruction), this should compile down to the <code>lock cmpxchg16b<\/code> instruction. If using the LLVM backend for building Zig, then this works. However, if you try and build the same Zig with the self-hosted compiler (the default for x86-64 debug builds, or selected by passing <code>-fno-llvm<\/code>), then it doesn&#8217;t work:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/opt\/zig-0.17.0\/zig build-obj \/repro\/src\/load128.zig -target x86_64-linux -mcpu=x86_64_v2 -O Debug -femit-bin=\/tmp\/load128\/zig-0.17.0\/load128.o\nrepro\/src\/load128.zig:2:8: error: genSetReg called with a value larger than dst_reg\nexport fn load128(p: *u128) u128 {\n~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~\nexit status: 1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s possible that the <a href=\"https:\/\/codeberg.org\/ziglang\/zig\/issues\/37289\">Codeberg issue #37289 issue, &#8220;&#8221;x86_64 backend: compiler segfault on <code>@cmpxchgStrong(u128, ...)<\/code> when the new value is an expression&#8221;&#8221;<\/a> is related (this was an open issue as of 2026-10-17).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I have not reported this upstream, as the <a href=\"https:\/\/ziglang.org\/code-of-conduct\/#strict-no-llm-no-ai-policy\">Zig Code of Conduct<\/a> is unambiguous about bugs found with the assistance of LLMs (see my <a href=\"https:\/\/www.flamingspork.com\/blog\/2026\/10\/26\/zig-self-hosted-backend-lowers-a-seq_cst-atomicrmw-xchg-with-a-discarded-result-to-a-plain-mov\/\">previous post<\/a> for details).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Previously, I wrote about how the Zig self-hosted backend lowers a seq_cst @atomicRmw(.Xchg) with a discarded result to a plain mov, but this isn&#8217;t the only issue with the self-hosted backend and atomics. A valid bit of Zig code is &hellip; <a href=\"https:\/\/www.flamingspork.com\/blog\/2026\/11\/05\/zig-self-hosted-compiler-cant-compile-a-16-byte-atomicload\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1],"tags":[],"class_list":["post-5056","post","type-post","status-publish","format-standard","hentry","category-general"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p5a6n8-1jy","jetpack-related-posts":[{"id":5040,"url":"https:\/\/www.flamingspork.com\/blog\/2026\/10\/26\/zig-self-hosted-backend-lowers-a-seq_cst-atomicrmw-xchg-with-a-discarded-result-to-a-plain-mov\/","url_meta":{"origin":5056,"position":0},"title":"Zig self-hosted backend lowers a seq_cst @atomicRmw(.Xchg) with a discarded result to a plain mov","author":"Stewart Smith","date":"2026-10-26","format":false,"excerpt":"Finding codegen bugs for atomic operations is fun! I'll note that I am not a Zig developer. I don't write code in the language, and I don't work on the language. I'm just some guy who decided to learn a bit about it. The Zig programming language has two compilers\u2026","rel":"","context":"In &quot;General&quot;","block_context":{"text":"General","link":"https:\/\/www.flamingspork.com\/blog\/category\/general\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.flamingspork.com\/blog\/wp-content\/uploads\/2026\/10\/image-2.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.flamingspork.com\/blog\/wp-content\/uploads\/2026\/10\/image-2.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.flamingspork.com\/blog\/wp-content\/uploads\/2026\/10\/image-2.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":101,"url":"https:\/\/www.flamingspork.com\/blog\/2003\/06\/04\/finally-fixed-it\/","url_meta":{"origin":5056,"position":1},"title":"finally! fixed it!","author":"Stewart Smith","date":"2003-06-04","format":false,"excerpt":"From: Stewart Smith Date: Wed Jun 4, 2003 3:56:09 PM Australia\/Melbourne To: Linus Torvalds Cc: linux-kernel@vger.kernel.org, David Woodhouse , Stewart Smith Subject: [PATCH] fixed: CRC32=y && 8193TOO=m unresolved symbols Linus, please apply - this fixes unresolved symbols when CONFIG_CRC32=y and CONFIG_8139TOO=m (it also appeared on some other ethernet device drivers).\u2026","rel":"","context":"In &quot;linux-kernel&quot;","block_context":{"text":"linux-kernel","link":"https:\/\/www.flamingspork.com\/blog\/category\/linux-kernel\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1361,"url":"https:\/\/www.flamingspork.com\/blog\/2009\/01\/22\/what-the-fuck-were-they-smoking\/","url_meta":{"origin":5056,"position":2},"title":"what the fuck were they smoking","author":"Stewart Smith","date":"2009-01-22","format":false,"excerpt":"\/* Function to free dboptions hash element *\/ extern \"C\" void free_dbopt(void *dbopt); void free_dbopt(void *dbopt) { free((unsigned char*) dbopt); }","rel":"","context":"In &quot;drizzle&quot;","block_context":{"text":"drizzle","link":"https:\/\/www.flamingspork.com\/blog\/category\/work-et-al\/drizzle-work-et-al\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":3906,"url":"https:\/\/www.flamingspork.com\/blog\/2014\/11\/14\/c-bitfields-considered-harmful\/","url_meta":{"origin":5056,"position":3},"title":"C bitfields considered harmful","author":"Stewart Smith","date":"2014-11-14","format":false,"excerpt":"In C (and C++) you can specify that a variable should take a specific number of bits of storage by doing \"uint32_t foo:4;\" rather than just \"uint32_t foo\". In this example, the former uses 4 bits while the latter uses 32bits. This can be useful to pack many bit fields\u2026","rel":"","context":"In &quot;code&quot;","block_context":{"text":"code","link":"https:\/\/www.flamingspork.com\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1262,"url":"https:\/\/www.flamingspork.com\/blog\/2008\/11\/22\/drop-table-fail-on-the-road-to-removing-the-frm\/","url_meta":{"origin":5056,"position":4},"title":"drop table fail (on the road to removing the FRM)","author":"Stewart Smith","date":"2008-11-22","format":false,"excerpt":"So... in removing the FRM file in Drizzle, I found a bit of a nugget on how drop table works (currently in the MySQL server and now \"did\" in Drizzle). If you DROP TABLE t1; this is what happens open the .frm file read first 10bytes (oh, and if you\u2026","rel":"","context":"In &quot;drizzle&quot;","block_context":{"text":"drizzle","link":"https:\/\/www.flamingspork.com\/blog\/category\/work-et-al\/drizzle-work-et-al\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1860,"url":"https:\/\/www.flamingspork.com\/blog\/2010\/03\/17\/a-md5-stored-procedure-for-drizzle-in-c\/","url_meta":{"origin":5056,"position":5},"title":"A MD5 stored procedure for Drizzle&#8230; in C","author":"Stewart Smith","date":"2010-03-17","format":false,"excerpt":"So, just in case that wasn't evil enough for you... perhaps you have something you want to know the MD5 checksum of. So, you could just do this: drizzle> select md5('Hello World!'); +----------------------------------+ | md5('Hello World!') | +----------------------------------+ | ed076287532e86365e841e92bfc50d8c | +----------------------------------+ 1 row in set (0 sec) But that\u2026","rel":"","context":"In &quot;code&quot;","block_context":{"text":"code","link":"https:\/\/www.flamingspork.com\/blog\/category\/code\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/posts\/5056","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/comments?post=5056"}],"version-history":[{"count":1,"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/posts\/5056\/revisions"}],"predecessor-version":[{"id":5057,"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/posts\/5056\/revisions\/5057"}],"wp:attachment":[{"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/media?parent=5056"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/categories?post=5056"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.flamingspork.com\/blog\/wp-json\/wp\/v2\/tags?post=5056"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}