Skip to content

sema: Support reinterpreting extern/packed unions at comptime via field access - #17352

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory
Oct 3, 2023
Merged

sema: Support reinterpreting extern/packed unions at comptime via field access#17352
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory

Conversation

@kcbanner

@kcbannerkcbanner commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

Closes#17311

My previous change for reading / writing to unions at comptime did not handle union field read / writes correctly in all cases. Previously, if a field was written to a union, it would overwrite the entire value. This is problematic when a field of a larger size is subsequently read, because the value would not be long enough, causing a panic.

Additionally, the writing behaviour itself was incorrect. Writing to a field of a packed or extern union should only overwrite the bits corresponding to that field, allowing for memory reintepretation via field writes / reads.

I addressed these problems as follows:

Add the concept of a "backing type" for extern / packed unions (Type.unionBackingType). For extern unions, this is a u8 array, for packed unions it's an integer matching the bitSize of the union. Whenever union memory is read at comptime, it's read as this type.

When union memory is written at comptime, the tag may still be known. If so, the memory is written using the tagged type. If the tag is unknown (because this union had previously been read from memory), it's simply written back out as the backing type.

I added write_packed to the reinterpret field of ComptimePtrMutationKit. This causes writes of the operand to be packed - which is necessary when writing to a field of a packed union. Without this, writing a value to a u1 field would overwrite the entire byte it occupied.

The final case to address was reading a different (potentially larger) field from a union when it was written with a known tag. To handle this, a new kind of bitcast was introduced (bitCastUnionFieldVal) which supports reading a larger field by using a backing buffer that has the unwritten bits set to undefined. The reason to support this (vs always just writing the union as it's backing type), is that no reads to larger fields ever occur at comptime, it would be strictly worse to have spent time writing the full backing type.

I added new tests cases to cover these cases. I originally wrote them to just run at comptime, but I thought it would be useful to check this behaviour at runtime as well, and discovered a couple issues there:

I've skipped some of the runtime portions of these new tests.

Other changes:

  • Payload.Union now has an optional tag value, to correctly support uninterning .none-tagged unions
  • TypedValue now prints the value of the backing storage of the union (either as a byte array, or an integer)

ie.

 %13!= dbg_var_val(<union.test.memset extern union at comptime.U, .{ (unknown tag) = "\x00" }>, "u")

…ld access
My previous change for reading / writing to unions at comptime did not handle
union field read/writes correctly in all cases. Previously, if a field was
written to a union, it would overwrite the entire value. This is problematic
when a field of a larger size is subsequently read, because the value would not
be long enough, causing a panic.
Additionally, the writing behaviour itself was incorrect. Writing to a field of
a packed or extern union should only overwrite the bits corresponding to that
field, allowing for memory reintepretation via field writes / reads.
I addressed these problems as follows:
Add the concept of a "backing type" for extern / packed unions
(`Type.unionBackingType`). For extern unions, this is a `u8` array, for packed
unions it's an integer matching the `bitSize` of the union. Whenever union
memory is read at comptime, it's read as this type.
When union memory is written at comptime, the tag may still be known. If so, the
memory is written using the tagged type. If the tag is unknown (because this
union had previously been read from memory), it's simply written back out as the
backing type.
I added `write_packed` to the `reinterpret` field of
`ComptimePtrMutationKit`. This causes writes of the operand to be packed - which
is necessary when writing to a field of a packed union. Without this, writing a
value to a `u1` field would overwrite the entire byte it occupied.
The final case to address was reading a different (potentially larger) field
from a union when it was written with a known tag. To handle this, a new kind of
bitcast was introduced (`bitCastUnionFieldVal`) which supports reading a larger
field by using a backing buffer that has the unwritten bits set to
undefined. The reason to support this (vs always just writing the union as it's
backing type), is that no reads to larger fields ever occur at comptime, it
would be strictly worse to have spent time writing the full backing type.
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from f5adcc2 to 3c317c6CompareOctober 2, 2023 17:15
… fields
Updated the tests to also run at runtime, and moved them to union.zig
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from 3c317c6 to fb33bc9CompareOctober 2, 2023 17:29
@andrewrk

Copy link
Copy Markdown
Member

Excellent work.

@andrewrk

Copy link
Copy Markdown
Member

The new behavior test caused #19389.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OOB panic when reading inactive field of a comptime var extern union

2 participants

@kcbanner@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
sema: Support reinterpreting extern/packed unions at comptime via field access by kcbanner · Pull Request #17352 · ziglang/zig · GitHub
Skip to content

sema: Support reinterpreting extern/packed unions at comptime via field access - #17352

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory
Oct 3, 2023
Merged

sema: Support reinterpreting extern/packed unions at comptime via field access#17352
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory

Conversation

@kcbanner

@kcbannerkcbanner commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

Closes#17311

My previous change for reading / writing to unions at comptime did not handle union field read / writes correctly in all cases. Previously, if a field was written to a union, it would overwrite the entire value. This is problematic when a field of a larger size is subsequently read, because the value would not be long enough, causing a panic.

Additionally, the writing behaviour itself was incorrect. Writing to a field of a packed or extern union should only overwrite the bits corresponding to that field, allowing for memory reintepretation via field writes / reads.

I addressed these problems as follows:

Add the concept of a "backing type" for extern / packed unions (Type.unionBackingType). For extern unions, this is a u8 array, for packed unions it's an integer matching the bitSize of the union. Whenever union memory is read at comptime, it's read as this type.

When union memory is written at comptime, the tag may still be known. If so, the memory is written using the tagged type. If the tag is unknown (because this union had previously been read from memory), it's simply written back out as the backing type.

I added write_packed to the reinterpret field of ComptimePtrMutationKit. This causes writes of the operand to be packed - which is necessary when writing to a field of a packed union. Without this, writing a value to a u1 field would overwrite the entire byte it occupied.

The final case to address was reading a different (potentially larger) field from a union when it was written with a known tag. To handle this, a new kind of bitcast was introduced (bitCastUnionFieldVal) which supports reading a larger field by using a backing buffer that has the unwritten bits set to undefined. The reason to support this (vs always just writing the union as it's backing type), is that no reads to larger fields ever occur at comptime, it would be strictly worse to have spent time writing the full backing type.

I added new tests cases to cover these cases. I originally wrote them to just run at comptime, but I thought it would be useful to check this behaviour at runtime as well, and discovered a couple issues there:

I've skipped some of the runtime portions of these new tests.

Other changes:

  • Payload.Union now has an optional tag value, to correctly support uninterning .none-tagged unions
  • TypedValue now prints the value of the backing storage of the union (either as a byte array, or an integer)

ie.

 %13!= dbg_var_val(<union.test.memset extern union at comptime.U, .{ (unknown tag) = "\x00" }>, "u")

…ld access
My previous change for reading / writing to unions at comptime did not handle
union field read/writes correctly in all cases. Previously, if a field was
written to a union, it would overwrite the entire value. This is problematic
when a field of a larger size is subsequently read, because the value would not
be long enough, causing a panic.
Additionally, the writing behaviour itself was incorrect. Writing to a field of
a packed or extern union should only overwrite the bits corresponding to that
field, allowing for memory reintepretation via field writes / reads.
I addressed these problems as follows:
Add the concept of a "backing type" for extern / packed unions
(`Type.unionBackingType`). For extern unions, this is a `u8` array, for packed
unions it's an integer matching the `bitSize` of the union. Whenever union
memory is read at comptime, it's read as this type.
When union memory is written at comptime, the tag may still be known. If so, the
memory is written using the tagged type. If the tag is unknown (because this
union had previously been read from memory), it's simply written back out as the
backing type.
I added `write_packed` to the `reinterpret` field of
`ComptimePtrMutationKit`. This causes writes of the operand to be packed - which
is necessary when writing to a field of a packed union. Without this, writing a
value to a `u1` field would overwrite the entire byte it occupied.
The final case to address was reading a different (potentially larger) field
from a union when it was written with a known tag. To handle this, a new kind of
bitcast was introduced (`bitCastUnionFieldVal`) which supports reading a larger
field by using a backing buffer that has the unwritten bits set to
undefined. The reason to support this (vs always just writing the union as it's
backing type), is that no reads to larger fields ever occur at comptime, it
would be strictly worse to have spent time writing the full backing type.
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from f5adcc2 to 3c317c6CompareOctober 2, 2023 17:15
… fields
Updated the tests to also run at runtime, and moved them to union.zig
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from 3c317c6 to fb33bc9CompareOctober 2, 2023 17:29
@andrewrk

Copy link
Copy Markdown
Member

Excellent work.

@andrewrk

Copy link
Copy Markdown
Member

The new behavior test caused #19389.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OOB panic when reading inactive field of a comptime var extern union

2 participants

@kcbanner@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' sema: Support reinterpreting extern/packed unions at comptime via field access by kcbanner · Pull Request #17352 · ziglang/zig · GitHub
Skip to content

sema: Support reinterpreting extern/packed unions at comptime via field access - #17352

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory
Oct 3, 2023
Merged

sema: Support reinterpreting extern/packed unions at comptime via field access#17352
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory

Conversation

@kcbanner

@kcbannerkcbanner commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

Closes#17311

My previous change for reading / writing to unions at comptime did not handle union field read / writes correctly in all cases. Previously, if a field was written to a union, it would overwrite the entire value. This is problematic when a field of a larger size is subsequently read, because the value would not be long enough, causing a panic.

Additionally, the writing behaviour itself was incorrect. Writing to a field of a packed or extern union should only overwrite the bits corresponding to that field, allowing for memory reintepretation via field writes / reads.

I addressed these problems as follows:

Add the concept of a "backing type" for extern / packed unions (Type.unionBackingType). For extern unions, this is a u8 array, for packed unions it's an integer matching the bitSize of the union. Whenever union memory is read at comptime, it's read as this type.

When union memory is written at comptime, the tag may still be known. If so, the memory is written using the tagged type. If the tag is unknown (because this union had previously been read from memory), it's simply written back out as the backing type.

I added write_packed to the reinterpret field of ComptimePtrMutationKit. This causes writes of the operand to be packed - which is necessary when writing to a field of a packed union. Without this, writing a value to a u1 field would overwrite the entire byte it occupied.

The final case to address was reading a different (potentially larger) field from a union when it was written with a known tag. To handle this, a new kind of bitcast was introduced (bitCastUnionFieldVal) which supports reading a larger field by using a backing buffer that has the unwritten bits set to undefined. The reason to support this (vs always just writing the union as it's backing type), is that no reads to larger fields ever occur at comptime, it would be strictly worse to have spent time writing the full backing type.

I added new tests cases to cover these cases. I originally wrote them to just run at comptime, but I thought it would be useful to check this behaviour at runtime as well, and discovered a couple issues there:

I've skipped some of the runtime portions of these new tests.

Other changes:

  • Payload.Union now has an optional tag value, to correctly support uninterning .none-tagged unions
  • TypedValue now prints the value of the backing storage of the union (either as a byte array, or an integer)

ie.

 %13!= dbg_var_val(<union.test.memset extern union at comptime.U, .{ (unknown tag) = "\x00" }>, "u")

…ld access
My previous change for reading / writing to unions at comptime did not handle
union field read/writes correctly in all cases. Previously, if a field was
written to a union, it would overwrite the entire value. This is problematic
when a field of a larger size is subsequently read, because the value would not
be long enough, causing a panic.
Additionally, the writing behaviour itself was incorrect. Writing to a field of
a packed or extern union should only overwrite the bits corresponding to that
field, allowing for memory reintepretation via field writes / reads.
I addressed these problems as follows:
Add the concept of a "backing type" for extern / packed unions
(`Type.unionBackingType`). For extern unions, this is a `u8` array, for packed
unions it's an integer matching the `bitSize` of the union. Whenever union
memory is read at comptime, it's read as this type.
When union memory is written at comptime, the tag may still be known. If so, the
memory is written using the tagged type. If the tag is unknown (because this
union had previously been read from memory), it's simply written back out as the
backing type.
I added `write_packed` to the `reinterpret` field of
`ComptimePtrMutationKit`. This causes writes of the operand to be packed - which
is necessary when writing to a field of a packed union. Without this, writing a
value to a `u1` field would overwrite the entire byte it occupied.
The final case to address was reading a different (potentially larger) field
from a union when it was written with a known tag. To handle this, a new kind of
bitcast was introduced (`bitCastUnionFieldVal`) which supports reading a larger
field by using a backing buffer that has the unwritten bits set to
undefined. The reason to support this (vs always just writing the union as it's
backing type), is that no reads to larger fields ever occur at comptime, it
would be strictly worse to have spent time writing the full backing type.
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from f5adcc2 to 3c317c6CompareOctober 2, 2023 17:15
… fields
Updated the tests to also run at runtime, and moved them to union.zig
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from 3c317c6 to fb33bc9CompareOctober 2, 2023 17:29
@andrewrk

Copy link
Copy Markdown
Member

Excellent work.

@andrewrk

Copy link
Copy Markdown
Member

The new behavior test caused #19389.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OOB panic when reading inactive field of a comptime var extern union

2 participants

@kcbanner@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' sema: Support reinterpreting extern/packed unions at comptime via field access by kcbanner · Pull Request #17352 · ziglang/zig · GitHub
Skip to content

sema: Support reinterpreting extern/packed unions at comptime via field access - #17352

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory
Oct 3, 2023
Merged

sema: Support reinterpreting extern/packed unions at comptime via field access#17352
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory

Conversation

@kcbanner

@kcbannerkcbanner commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

Closes#17311

My previous change for reading / writing to unions at comptime did not handle union field read / writes correctly in all cases. Previously, if a field was written to a union, it would overwrite the entire value. This is problematic when a field of a larger size is subsequently read, because the value would not be long enough, causing a panic.

Additionally, the writing behaviour itself was incorrect. Writing to a field of a packed or extern union should only overwrite the bits corresponding to that field, allowing for memory reintepretation via field writes / reads.

I addressed these problems as follows:

Add the concept of a "backing type" for extern / packed unions (Type.unionBackingType). For extern unions, this is a u8 array, for packed unions it's an integer matching the bitSize of the union. Whenever union memory is read at comptime, it's read as this type.

When union memory is written at comptime, the tag may still be known. If so, the memory is written using the tagged type. If the tag is unknown (because this union had previously been read from memory), it's simply written back out as the backing type.

I added write_packed to the reinterpret field of ComptimePtrMutationKit. This causes writes of the operand to be packed - which is necessary when writing to a field of a packed union. Without this, writing a value to a u1 field would overwrite the entire byte it occupied.

The final case to address was reading a different (potentially larger) field from a union when it was written with a known tag. To handle this, a new kind of bitcast was introduced (bitCastUnionFieldVal) which supports reading a larger field by using a backing buffer that has the unwritten bits set to undefined. The reason to support this (vs always just writing the union as it's backing type), is that no reads to larger fields ever occur at comptime, it would be strictly worse to have spent time writing the full backing type.

I added new tests cases to cover these cases. I originally wrote them to just run at comptime, but I thought it would be useful to check this behaviour at runtime as well, and discovered a couple issues there:

I've skipped some of the runtime portions of these new tests.

Other changes:

  • Payload.Union now has an optional tag value, to correctly support uninterning .none-tagged unions
  • TypedValue now prints the value of the backing storage of the union (either as a byte array, or an integer)

ie.

 %13!= dbg_var_val(<union.test.memset extern union at comptime.U, .{ (unknown tag) = "\x00" }>, "u")

…ld access
My previous change for reading / writing to unions at comptime did not handle
union field read/writes correctly in all cases. Previously, if a field was
written to a union, it would overwrite the entire value. This is problematic
when a field of a larger size is subsequently read, because the value would not
be long enough, causing a panic.
Additionally, the writing behaviour itself was incorrect. Writing to a field of
a packed or extern union should only overwrite the bits corresponding to that
field, allowing for memory reintepretation via field writes / reads.
I addressed these problems as follows:
Add the concept of a "backing type" for extern / packed unions
(`Type.unionBackingType`). For extern unions, this is a `u8` array, for packed
unions it's an integer matching the `bitSize` of the union. Whenever union
memory is read at comptime, it's read as this type.
When union memory is written at comptime, the tag may still be known. If so, the
memory is written using the tagged type. If the tag is unknown (because this
union had previously been read from memory), it's simply written back out as the
backing type.
I added `write_packed` to the `reinterpret` field of
`ComptimePtrMutationKit`. This causes writes of the operand to be packed - which
is necessary when writing to a field of a packed union. Without this, writing a
value to a `u1` field would overwrite the entire byte it occupied.
The final case to address was reading a different (potentially larger) field
from a union when it was written with a known tag. To handle this, a new kind of
bitcast was introduced (`bitCastUnionFieldVal`) which supports reading a larger
field by using a backing buffer that has the unwritten bits set to
undefined. The reason to support this (vs always just writing the union as it's
backing type), is that no reads to larger fields ever occur at comptime, it
would be strictly worse to have spent time writing the full backing type.
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from f5adcc2 to 3c317c6CompareOctober 2, 2023 17:15
… fields
Updated the tests to also run at runtime, and moved them to union.zig
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from 3c317c6 to fb33bc9CompareOctober 2, 2023 17:29
@andrewrk

Copy link
Copy Markdown
Member

Excellent work.

@andrewrk

Copy link
Copy Markdown
Member

The new behavior test caused #19389.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OOB panic when reading inactive field of a comptime var extern union

2 participants

@kcbanner@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' sema: Support reinterpreting extern/packed unions at comptime via field access by kcbanner · Pull Request #17352 · ziglang/zig · GitHub
Skip to content

sema: Support reinterpreting extern/packed unions at comptime via field access - #17352

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory
Oct 3, 2023
Merged

sema: Support reinterpreting extern/packed unions at comptime via field access#17352
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory

Conversation

@kcbanner

@kcbannerkcbanner commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

Closes#17311

My previous change for reading / writing to unions at comptime did not handle union field read / writes correctly in all cases. Previously, if a field was written to a union, it would overwrite the entire value. This is problematic when a field of a larger size is subsequently read, because the value would not be long enough, causing a panic.

Additionally, the writing behaviour itself was incorrect. Writing to a field of a packed or extern union should only overwrite the bits corresponding to that field, allowing for memory reintepretation via field writes / reads.

I addressed these problems as follows:

Add the concept of a "backing type" for extern / packed unions (Type.unionBackingType). For extern unions, this is a u8 array, for packed unions it's an integer matching the bitSize of the union. Whenever union memory is read at comptime, it's read as this type.

When union memory is written at comptime, the tag may still be known. If so, the memory is written using the tagged type. If the tag is unknown (because this union had previously been read from memory), it's simply written back out as the backing type.

I added write_packed to the reinterpret field of ComptimePtrMutationKit. This causes writes of the operand to be packed - which is necessary when writing to a field of a packed union. Without this, writing a value to a u1 field would overwrite the entire byte it occupied.

The final case to address was reading a different (potentially larger) field from a union when it was written with a known tag. To handle this, a new kind of bitcast was introduced (bitCastUnionFieldVal) which supports reading a larger field by using a backing buffer that has the unwritten bits set to undefined. The reason to support this (vs always just writing the union as it's backing type), is that no reads to larger fields ever occur at comptime, it would be strictly worse to have spent time writing the full backing type.

I added new tests cases to cover these cases. I originally wrote them to just run at comptime, but I thought it would be useful to check this behaviour at runtime as well, and discovered a couple issues there:

I've skipped some of the runtime portions of these new tests.

Other changes:

  • Payload.Union now has an optional tag value, to correctly support uninterning .none-tagged unions
  • TypedValue now prints the value of the backing storage of the union (either as a byte array, or an integer)

ie.

 %13!= dbg_var_val(<union.test.memset extern union at comptime.U, .{ (unknown tag) = "\x00" }>, "u")

…ld access
My previous change for reading / writing to unions at comptime did not handle
union field read/writes correctly in all cases. Previously, if a field was
written to a union, it would overwrite the entire value. This is problematic
when a field of a larger size is subsequently read, because the value would not
be long enough, causing a panic.
Additionally, the writing behaviour itself was incorrect. Writing to a field of
a packed or extern union should only overwrite the bits corresponding to that
field, allowing for memory reintepretation via field writes / reads.
I addressed these problems as follows:
Add the concept of a "backing type" for extern / packed unions
(`Type.unionBackingType`). For extern unions, this is a `u8` array, for packed
unions it's an integer matching the `bitSize` of the union. Whenever union
memory is read at comptime, it's read as this type.
When union memory is written at comptime, the tag may still be known. If so, the
memory is written using the tagged type. If the tag is unknown (because this
union had previously been read from memory), it's simply written back out as the
backing type.
I added `write_packed` to the `reinterpret` field of
`ComptimePtrMutationKit`. This causes writes of the operand to be packed - which
is necessary when writing to a field of a packed union. Without this, writing a
value to a `u1` field would overwrite the entire byte it occupied.
The final case to address was reading a different (potentially larger) field
from a union when it was written with a known tag. To handle this, a new kind of
bitcast was introduced (`bitCastUnionFieldVal`) which supports reading a larger
field by using a backing buffer that has the unwritten bits set to
undefined. The reason to support this (vs always just writing the union as it's
backing type), is that no reads to larger fields ever occur at comptime, it
would be strictly worse to have spent time writing the full backing type.
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from f5adcc2 to 3c317c6CompareOctober 2, 2023 17:15
… fields
Updated the tests to also run at runtime, and moved them to union.zig
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from 3c317c6 to fb33bc9CompareOctober 2, 2023 17:29
@andrewrk

Copy link
Copy Markdown
Member

Excellent work.

@andrewrk

Copy link
Copy Markdown
Member

The new behavior test caused #19389.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OOB panic when reading inactive field of a comptime var extern union

2 participants

@kcbanner@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' sema: Support reinterpreting extern/packed unions at comptime via field access by kcbanner · Pull Request #17352 · ziglang/zig · GitHub
Skip to content

sema: Support reinterpreting extern/packed unions at comptime via field access - #17352

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory
Oct 3, 2023
Merged

sema: Support reinterpreting extern/packed unions at comptime via field access#17352
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory

Conversation

@kcbanner

@kcbannerkcbanner commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

Closes#17311

My previous change for reading / writing to unions at comptime did not handle union field read / writes correctly in all cases. Previously, if a field was written to a union, it would overwrite the entire value. This is problematic when a field of a larger size is subsequently read, because the value would not be long enough, causing a panic.

Additionally, the writing behaviour itself was incorrect. Writing to a field of a packed or extern union should only overwrite the bits corresponding to that field, allowing for memory reintepretation via field writes / reads.

I addressed these problems as follows:

Add the concept of a "backing type" for extern / packed unions (Type.unionBackingType). For extern unions, this is a u8 array, for packed unions it's an integer matching the bitSize of the union. Whenever union memory is read at comptime, it's read as this type.

When union memory is written at comptime, the tag may still be known. If so, the memory is written using the tagged type. If the tag is unknown (because this union had previously been read from memory), it's simply written back out as the backing type.

I added write_packed to the reinterpret field of ComptimePtrMutationKit. This causes writes of the operand to be packed - which is necessary when writing to a field of a packed union. Without this, writing a value to a u1 field would overwrite the entire byte it occupied.

The final case to address was reading a different (potentially larger) field from a union when it was written with a known tag. To handle this, a new kind of bitcast was introduced (bitCastUnionFieldVal) which supports reading a larger field by using a backing buffer that has the unwritten bits set to undefined. The reason to support this (vs always just writing the union as it's backing type), is that no reads to larger fields ever occur at comptime, it would be strictly worse to have spent time writing the full backing type.

I added new tests cases to cover these cases. I originally wrote them to just run at comptime, but I thought it would be useful to check this behaviour at runtime as well, and discovered a couple issues there:

I've skipped some of the runtime portions of these new tests.

Other changes:

  • Payload.Union now has an optional tag value, to correctly support uninterning .none-tagged unions
  • TypedValue now prints the value of the backing storage of the union (either as a byte array, or an integer)

ie.

 %13!= dbg_var_val(<union.test.memset extern union at comptime.U, .{ (unknown tag) = "\x00" }>, "u")

…ld access
My previous change for reading / writing to unions at comptime did not handle
union field read/writes correctly in all cases. Previously, if a field was
written to a union, it would overwrite the entire value. This is problematic
when a field of a larger size is subsequently read, because the value would not
be long enough, causing a panic.
Additionally, the writing behaviour itself was incorrect. Writing to a field of
a packed or extern union should only overwrite the bits corresponding to that
field, allowing for memory reintepretation via field writes / reads.
I addressed these problems as follows:
Add the concept of a "backing type" for extern / packed unions
(`Type.unionBackingType`). For extern unions, this is a `u8` array, for packed
unions it's an integer matching the `bitSize` of the union. Whenever union
memory is read at comptime, it's read as this type.
When union memory is written at comptime, the tag may still be known. If so, the
memory is written using the tagged type. If the tag is unknown (because this
union had previously been read from memory), it's simply written back out as the
backing type.
I added `write_packed` to the `reinterpret` field of
`ComptimePtrMutationKit`. This causes writes of the operand to be packed - which
is necessary when writing to a field of a packed union. Without this, writing a
value to a `u1` field would overwrite the entire byte it occupied.
The final case to address was reading a different (potentially larger) field
from a union when it was written with a known tag. To handle this, a new kind of
bitcast was introduced (`bitCastUnionFieldVal`) which supports reading a larger
field by using a backing buffer that has the unwritten bits set to
undefined. The reason to support this (vs always just writing the union as it's
backing type), is that no reads to larger fields ever occur at comptime, it
would be strictly worse to have spent time writing the full backing type.
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from f5adcc2 to 3c317c6CompareOctober 2, 2023 17:15
… fields
Updated the tests to also run at runtime, and moved them to union.zig
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from 3c317c6 to fb33bc9CompareOctober 2, 2023 17:29
@andrewrk

Copy link
Copy Markdown
Member

Excellent work.

@andrewrk

Copy link
Copy Markdown
Member

The new behavior test caused #19389.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OOB panic when reading inactive field of a comptime var extern union

2 participants

@kcbanner@andrewrk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); sema: Support reinterpreting extern/packed unions at comptime via field access by kcbanner · Pull Request #17352 · ziglang/zig · GitHub
Skip to content

sema: Support reinterpreting extern/packed unions at comptime via field access - #17352

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory
Oct 3, 2023
Merged

sema: Support reinterpreting extern/packed unions at comptime via field access#17352
andrewrk merged 3 commits into
ziglang:masterfrom
kcbanner:extern_union_comptime_memory

Conversation

@kcbanner

@kcbannerkcbanner commented Oct 1, 2023

Copy link
Copy Markdown
Contributor

Closes#17311

My previous change for reading / writing to unions at comptime did not handle union field read / writes correctly in all cases. Previously, if a field was written to a union, it would overwrite the entire value. This is problematic when a field of a larger size is subsequently read, because the value would not be long enough, causing a panic.

Additionally, the writing behaviour itself was incorrect. Writing to a field of a packed or extern union should only overwrite the bits corresponding to that field, allowing for memory reintepretation via field writes / reads.

I addressed these problems as follows:

Add the concept of a "backing type" for extern / packed unions (Type.unionBackingType). For extern unions, this is a u8 array, for packed unions it's an integer matching the bitSize of the union. Whenever union memory is read at comptime, it's read as this type.

When union memory is written at comptime, the tag may still be known. If so, the memory is written using the tagged type. If the tag is unknown (because this union had previously been read from memory), it's simply written back out as the backing type.

I added write_packed to the reinterpret field of ComptimePtrMutationKit. This causes writes of the operand to be packed - which is necessary when writing to a field of a packed union. Without this, writing a value to a u1 field would overwrite the entire byte it occupied.

The final case to address was reading a different (potentially larger) field from a union when it was written with a known tag. To handle this, a new kind of bitcast was introduced (bitCastUnionFieldVal) which supports reading a larger field by using a backing buffer that has the unwritten bits set to undefined. The reason to support this (vs always just writing the union as it's backing type), is that no reads to larger fields ever occur at comptime, it would be strictly worse to have spent time writing the full backing type.

I added new tests cases to cover these cases. I originally wrote them to just run at comptime, but I thought it would be useful to check this behaviour at runtime as well, and discovered a couple issues there:

I've skipped some of the runtime portions of these new tests.

Other changes:

  • Payload.Union now has an optional tag value, to correctly support uninterning .none-tagged unions
  • TypedValue now prints the value of the backing storage of the union (either as a byte array, or an integer)

ie.

 %13!= dbg_var_val(<union.test.memset extern union at comptime.U, .{ (unknown tag) = "\x00" }>, "u")

…ld access
My previous change for reading / writing to unions at comptime did not handle
union field read/writes correctly in all cases. Previously, if a field was
written to a union, it would overwrite the entire value. This is problematic
when a field of a larger size is subsequently read, because the value would not
be long enough, causing a panic.
Additionally, the writing behaviour itself was incorrect. Writing to a field of
a packed or extern union should only overwrite the bits corresponding to that
field, allowing for memory reintepretation via field writes / reads.
I addressed these problems as follows:
Add the concept of a "backing type" for extern / packed unions
(`Type.unionBackingType`). For extern unions, this is a `u8` array, for packed
unions it's an integer matching the `bitSize` of the union. Whenever union
memory is read at comptime, it's read as this type.
When union memory is written at comptime, the tag may still be known. If so, the
memory is written using the tagged type. If the tag is unknown (because this
union had previously been read from memory), it's simply written back out as the
backing type.
I added `write_packed` to the `reinterpret` field of
`ComptimePtrMutationKit`. This causes writes of the operand to be packed - which
is necessary when writing to a field of a packed union. Without this, writing a
value to a `u1` field would overwrite the entire byte it occupied.
The final case to address was reading a different (potentially larger) field
from a union when it was written with a known tag. To handle this, a new kind of
bitcast was introduced (`bitCastUnionFieldVal`) which supports reading a larger
field by using a backing buffer that has the unwritten bits set to
undefined. The reason to support this (vs always just writing the union as it's
backing type), is that no reads to larger fields ever occur at comptime, it
would be strictly worse to have spent time writing the full backing type.
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from f5adcc2 to 3c317c6CompareOctober 2, 2023 17:15
… fields
Updated the tests to also run at runtime, and moved them to union.zig
@kcbanner
kcbannerforce-pushed the extern_union_comptime_memory branch from 3c317c6 to fb33bc9CompareOctober 2, 2023 17:29
@andrewrk

Copy link
Copy Markdown
Member

Excellent work.

@andrewrk

Copy link
Copy Markdown
Member

The new behavior test caused #19389.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OOB panic when reading inactive field of a comptime var extern union

2 participants

@kcbanner@andrewrk