Syntax for caller-visible stack setup #8159
-
This is an alternate (but non-conflicting) solution for the coding problem that inspired #8156. In that issue, I mention that adding The core idea is that, just as you can annotate a local variable declaration in a method with a
In this scenario, a change that adds an One other possible addition to this protocol would be to allow an I would not recommend allowing this syntax on A modification of the public class CaseInsensitiveOrderPreservingStringDictionary<TValue>
{
private List<string> keys = new();
private List<TValue> values = new();
public int Count => keys.Count;
// A reference to a dictionary location that has just been looked up, can index this dictionary
public readonly ref struct Slot
{
public required readonly string Key { get; init; }
public required int Index { get; init; }
public bool HasValue => Index >= 0;
}
public TValue this[string key]
{
get => TryGetSlot(key, out Slot slot) ? GetFromSlot(slot) : throw new KeyNotFoundException();
set => this[GetSlot(key)] = value;
}
public TValue this[Slot slot]
{
get => GetFromSlot(slot);
set => SetFromSlot(ref slot, value);
}
public bool ContainsKey(string key, int index = 0)
{
// declaring both parameters as ref-able
out var key, index;
GetSlot(ref key, ref index).HasValue;
}
public bool TryGetValue(string key, out TValue value)
{
// The key parameter is ref-able
out var key;
// The slot variable is out-able
out Slot slot;
GetSlot(ref key, out slot);
if (slot.HasValue) {
value = this[slot];
return true;
} else {
value = default;
return false;
}
}
public bool TryAdd(string key, TValue value)
{
out var key, value;
out Slot slot;
GetSlot(ref key, out slot)
if (!slot.HasValue) {
SetFromSlot(ref slot, value);
slot = targetSlot;
return true;
} else {
value = GetFromSlot(slot);
return false;
}
}
public bool TryGetSlot(string key, out Slot slot, int index = 0)
{
out var key, index;
slot = GetSlot(ref key, ref index);
return slot.HasValue;
}
protected Slot GetSlot(ref string key, ref int index = 0)
{
key = key.ToLowerInvariant();
index = keys.IndexOf(key, index);
return new Slot
{
Key = key,
Index = index,
};
}
protected TValue GetFromSlot(Slot slot)
{
return slot.HasValue ? values[slot.Index] : throw new KeyNotFoundException();
}
protected void SetFromSlot(ref Slot slot, TValue value)
{
if (slot.HasValue) {
values[slot.Index] = value;
} else {
slot = slot with { Index = Count };
keys.Add(slot.Key);
values.Add(value);
}
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 26 replies
-
I really don't understand what this proposal is attempting to achieve, but the notion that somehow a local can silently leak to the outside scope and somehow not impact the signature of the method is very unappealing to me. I don't even understand how such a thing would be achieved. Either the Also, inlining and assumptions about the interactions between methods bodies is something the compiler very explicitly doesn't get involved with. Every method is opaque and the IL represents the intent of the code, it doesn't shuffle stuff around for the sake of optimization because the compiler never has the full picture to do so. Inlining and the like is a runtime concern. |
Beta Was this translation helpful? Give feedback.
Yes, which is a requirement in order to meet the platform ABI and ensure the register allocator can do its job. While you could say "this method has a custom ABI and also uses
r8
to return a second result", that is overall worse for performance in the typical case.Spilling to the stack is not a problem, especially for almost any CPU that's shipped in the past 14 or so years. CPUs have long had support for "mirroring" stack spills into part of the internal register f…