abrown opened Issue #1385:
This is an open-ended issue for re-designing Cranelift's constant pools. In #1377 I added the ability to calculate the address of constants using
const_addr
and to declare constants in function preambles (e..gconst42 = [0 1 2...]
). Constant pools are still implemented at the function level, though, so there is no coalescing of constant values across functions.I added the function-level
ConstantPool
implementation in order to support SIMD constants (which don't fit in the immediate fields of Cranelift's IR). This is not a pressing issue yet, but before other components start using constants, it might be good to discuss:
- should we implement constant pools at the global level and what would this look like?
- what should the relocation hooks look like for a more general constant pool implementation? Currently the interface looks like
RelocSink::reloc_constant(&mut self, CodeOffset, Reloc, ConstantOffset)
and this may need to change.
abrown labeled Issue #1385:
This is an open-ended issue for re-designing Cranelift's constant pools. In #1377 I added the ability to calculate the address of constants using
const_addr
and to declare constants in function preambles (e..gconst42 = [0 1 2...]
). Constant pools are still implemented at the function level, though, so there is no coalescing of constant values across functions.I added the function-level
ConstantPool
implementation in order to support SIMD constants (which don't fit in the immediate fields of Cranelift's IR). This is not a pressing issue yet, but before other components start using constants, it might be good to discuss:
- should we implement constant pools at the global level and what would this look like?
- what should the relocation hooks look like for a more general constant pool implementation? Currently the interface looks like
RelocSink::reloc_constant(&mut self, CodeOffset, Reloc, ConstantOffset)
and this may need to change.
abrown commented on Issue #1385:
@cfallin, now that I think I understand the new backend better, I think this issue can be moved a bit further:
How things currently work in the new backend:
- at the emission level,
MachBuffer::pending_constants
is the vector containing the constant values; eventually these get emitted into the buffer and theMachLabel
s are fixed upMachBuffer::defer_constant(label, align, data, max_distance)
is the user-accessible way to add a constant during emission (e.g. see uses ofsink.defer_constant...
inemit.rs
)Inst::gen_constant
is the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64
in sizeso there are problems:
Inst::gen_constant
cannot be used to generate vector valuesMachBuffer::defer_constant
will duplicate constant valuesconst_addr
(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabel
to use inMachBuffer; to implement this instruction, we would have to search through
MachBuffer::pending_constantsto find a value that matched the
ConstantDatabehind its
const_addr's
Constant` handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constant
to accept aDataValue
instead of au64
(involves fixing up 9 uses of this method)- [ ] search through
lower.rs
to see other places that should be usinggen_constant
but are not (e.g. usingPXOR
directly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD
) and all 1s (CMPPS/PCMPEQ
)- [ ] modify
Inst::XmmLoadConstSeq
to becomeXmmLoadConst
--instead of emitting the vector inline with the function code it would usesink.defer_constant
all of this suggests some related refactoring:
- remove
LowerCtx::get_constant
:LowerCtx::get_constant
andLowerCtx::get_immediate
are doing the same thing except thatget_constant
returns au64
instead of the largerDataValue
- this affects
LowerCtx::get_input
, which returns aLowerInput
with aconstant: Option<u64>
field--this field could go away (12 uses), replaced byctx.get_immediate(...)
To keep ConstantPool's de-duplicated values AND allow const_addr to retrieve the address of a constant:
MachBuffer::defer_constant
should keep a mapping of constant values toMachLabels
(e.g. aHashMap
?) and only return theMachLabel
for a given value, de-duplicating them- this is redundant work since
ConstantPool
has already done this, but it is necessary work because lowering could introduce new constant values- I struggle with how to approach this inefficiency (suggestions?): this approach would mean that entire values would have to hashed/compared every time they are used (unlike
ConstantPool
, which usesConstant(u32)
handles)... but perhaps this could be optimized in the future- perhaps we can even genericize the ConstantPool and reuse it?
When the old backend goes away:
ConstantPool
can be simplified:ConstantPool::get_offset
,ConstantPool::set_offset
,binemit::const_disp4
can all go away since they are only necessary to work around old backend quirks.
abrown edited a comment on Issue #1385:
@cfallin, now that I think I understand the new backend better, I think this issue can be moved a bit further:
How things currently work in the new backend:
- at the emission level,
MachBuffer::pending_constants
is the vector containing the constant values; eventually these get emitted into the buffer and theMachLabel
s are fixed upMachBuffer::defer_constant(label, align, data, max_distance)
is the user-accessible way to add a constant during emission (e.g. see uses ofsink.defer_constant...
inemit.rs
)Inst::gen_constant
is the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64
in sizeso there are problems:
Inst::gen_constant
cannot be used to generate vector valuesMachBuffer::defer_constant
will duplicate constant valuesconst_addr
(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabel
to use inMachBuffer; to implement this instruction, we would have to search through
MachBuffer::pending_constantsto find a value that matched the
ConstantDatabehind its
const_addr's
Constant` handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constant
to accept aDataValue
instead of au64
(involves fixing up 9 uses of this method)- [ ] search through
lower.rs
to see other places that should be usinggen_constant
but are not (e.g. usingPXOR
directly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD
) and all 1s (CMPPS/PCMPEQ
)- [ ] modify
Inst::XmmLoadConstSeq
to becomeXmmLoadConst
--instead of emitting the vector inline with the function code it would usesink.defer_constant
all of this suggests some related refactoring:
- remove
LowerCtx::get_constant
:LowerCtx::get_constant
andLowerCtx::get_immediate
are doing the same thing except thatget_constant
returns au64
instead of the largerDataValue
- this affects
LowerCtx::get_input
, which returns aLowerInput
with aconstant: Option<u64>
field--this field could go away (12 uses), replaced byctx.get_immediate(...)
To keep ConstantPool's de-duplicated values AND allow const_addr to retrieve the address of a constant:
- [ ]
MachBuffer::defer_constant
should keep a mapping of constant values toMachLabels
(e.g. aHashMap
?) and only return theMachLabel
for a given value, de-duplicating them- this is redundant work since
ConstantPool
has already done this, but it is necessary work because lowering could introduce new constant values- I struggle with how to approach this inefficiency (suggestions?): this approach would mean that entire values would have to hashed/compared every time they are used (unlike
ConstantPool
, which usesConstant(u32)
handles)... but perhaps this could be optimized in the future- perhaps we can even genericize the ConstantPool and reuse it?
When the old backend goes away:
ConstantPool
can be simplified:ConstantPool::get_offset
,ConstantPool::set_offset
,binemit::const_disp4
can all go away since they are only necessary to work around old backend quirks.
abrown edited a comment on Issue #1385:
@cfallin, now that I think I understand the new backend better, I think this issue can be moved a bit further:
How things currently work in the new backend:
- at the emission level,
MachBuffer::pending_constants
is the vector containing the constant values; eventually these get emitted into the buffer and theMachLabel
s are fixed upMachBuffer::defer_constant(label, align, data, max_distance)
is the user-accessible way to add a constant during emission (e.g. see uses ofsink.defer_constant...
inemit.rs
)Inst::gen_constant
is the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64
in sizeso there are problems:
Inst::gen_constant
cannot be used to generate vector valuesMachBuffer::defer_constant
will duplicate constant valuesconst_addr
(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabel
to use inMachBuffer
; to implement this instruction, we would have to search throughMachBuffer::pending_constants
to find a value that matched theConstantData
behind itsconst_addr
'sConstant
handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constant
to accept aDataValue
instead of au64
(involves fixing up 9 uses of this method)- [ ] search through
lower.rs
to see other places that should be usinggen_constant
but are not (e.g. usingPXOR
directly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD
) and all 1s (CMPPS/PCMPEQ
)- [ ] modify
Inst::XmmLoadConstSeq
to becomeXmmLoadConst
--instead of emitting the vector inline with the function code it would usesink.defer_constant
all of this suggests some related refactoring:
- remove
LowerCtx::get_constant
:LowerCtx::get_constant
andLowerCtx::get_immediate
are doing the same thing except thatget_constant
returns au64
instead of the largerDataValue
- this affects
LowerCtx::get_input
, which returns aLowerInput
with aconstant: Option<u64>
field--this field could go away (12 uses), replaced byctx.get_immediate(...)
To keep ConstantPool's de-duplicated values AND allow const_addr to retrieve the address of a constant:
- [ ]
MachBuffer::defer_constant
should keep a mapping of constant values toMachLabels
(e.g. aHashMap
?) and only return theMachLabel
for a given value, de-duplicating them- this is redundant work since
ConstantPool
has already done this, but it is necessary work because lowering could introduce new constant values- I struggle with how to approach this inefficiency (suggestions?): this approach would mean that entire values would have to hashed/compared every time they are used (unlike
ConstantPool
, which usesConstant(u32)
handles)... but perhaps this could be optimized in the future- perhaps we can even genericize the ConstantPool and reuse it?
When the old backend goes away:
ConstantPool
can be simplified:ConstantPool::get_offset
,ConstantPool::set_offset
,binemit::const_disp4
can all go away since they are only necessary to work around old backend quirks.
cfallin commented on Issue #1385:
Hi @abrown -- thanks for the detailed look into this!
MachBuffer::defer_constant
is indeed the way forward here; the infra is built to emit constant pools, but just isn't wired up yet.The API expects the client (so, the lowering code) to allocate its own labels, and specify the label for each constant. So the most straightforward thing to do here would be, for constants in the
ConstantPool
, make an initial pass over them and allocate labels, keeping them in a table. Then the lookup from constant-pool handle toMachLabel
is just an array access. Or, slightly better, we lazily emit deferred constants, and the lookup table holdsOption<MachLabel>
.I think we should probably build that first before deduplication at the MachInst-lowering level; I'm not fully convinced that the overhead of a hashmap lookup on every new constant generation is worth the potential code-size reduction (but we should measure).
So I would say we should:
- Clean up the constant-related APIs on
x64::MachInst
andLowerCtx
, as you suggest- Add a table to the
LowerCtx
tracking labels for every constant, emitting on first request. Then we have something likeLowerCtx::constant_pool_label(...)
.- Eventually, deduplicate inside
LowerCtx
if we find it to be worthwhile.
abrown commented on Issue #1385:
Yeah, I like this better. I'll try it out and see what happens!
abrown edited a comment on Issue #1385:
@cfallin, now that I think I understand the new backend better, I think this issue can be moved a bit further:
How things currently work in the new backend:
- at the emission level,
MachBuffer::pending_constants
is the vector containing the constant values; eventually these get emitted into the buffer and theMachLabel
s are fixed upMachBuffer::defer_constant(label, align, data, max_distance)
is the user-accessible way to add a constant during emission (e.g. see uses ofsink.defer_constant...
inemit.rs
)Inst::gen_constant
is the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64
in sizeso there are problems:
Inst::gen_constant
cannot be used to generate vector valuesMachBuffer::defer_constant
will duplicate constant valuesconst_addr
(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabel
to use inMachBuffer
; to implement this instruction, we would have to search throughMachBuffer::pending_constants
to find a value that matched theConstantData
behind itsconst_addr
'sConstant
handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constant
to accept aDataValue
instead of au64
(involves fixing up 9 uses of this method)- [ ] search through
lower.rs
to see other places that should be usinggen_constant
but are not (e.g. usingPXOR
directly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD
) and all 1s (CMPPS/PCMPEQ
)- [ ] modify
Inst::XmmLoadConstSeq
to becomeXmmLoadConst
--instead of emitting the vector inline with the function code it would usesink.defer_constant
all of this suggests some related refactoring:
- remove
LowerCtx::get_constant
:LowerCtx::get_constant
andLowerCtx::get_immediate
are doing the same thing except thatget_constant
returns au64
instead of the largerDataValue
- this affects
LowerCtx::get_input
, which returns aLowerInput
with aconstant: Option<u64>
field--this field could go away (12 uses), replaced byctx.get_immediate(...)
To keep ConstantPool's de-duplicated values AND allow const_addr to retrieve the address of a constant:
- [x]
MachBuffer::defer_constant
should keep a mapping of constant values toMachLabels
(e.g. aHashMap
?) and only return theMachLabel
for a given value, de-duplicating them- this is redundant work since
ConstantPool
has already done this, but it is necessary work because lowering could introduce new constant values- I struggle with how to approach this inefficiency (suggestions?): this approach would mean that entire values would have to hashed/compared every time they are used (unlike
ConstantPool
, which usesConstant(u32)
handles)... but perhaps this could be optimized in the future- perhaps we can even genericize the ConstantPool and reuse it?
When the old backend goes away:
ConstantPool
can be simplified:ConstantPool::get_offset
,ConstantPool::set_offset
,binemit::const_disp4
can all go away since they are only necessary to work around old backend quirks.
abrown edited a comment on Issue #1385:
@cfallin, now that I think I understand the new backend better, I think this issue can be moved a bit further:
How things currently work in the new backend:
- at the emission level,
MachBuffer::pending_constants
is the vector containing the constant values; eventually these get emitted into the buffer and theMachLabel
s are fixed upMachBuffer::defer_constant(label, align, data, max_distance)
is the user-accessible way to add a constant during emission (e.g. see uses ofsink.defer_constant...
inemit.rs
)Inst::gen_constant
is the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64
in sizeso there are problems:
Inst::gen_constant
cannot be used to generate vector valuesMachBuffer::defer_constant
will duplicate constant valuesconst_addr
(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabel
to use inMachBuffer
; to implement this instruction, we would have to search throughMachBuffer::pending_constants
to find a value that matched theConstantData
behind itsconst_addr
'sConstant
handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constant
to accept aDataValue
instead of au64
(involves fixing up 9 uses of this method)- [ ] search through
lower.rs
to see other places that should be usinggen_constant
but are not (e.g. usingPXOR
directly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD
) and all 1s (CMPPS/PCMPEQ
)- [x] modify
Inst::XmmLoadConstSeq
to becomeXmmLoadConst
--instead of emitting the vector inline with the function code it would usesink.defer_constant
all of this suggests some related refactoring:
- remove
LowerCtx::get_constant
:LowerCtx::get_constant
andLowerCtx::get_immediate
are doing the same thing except thatget_constant
returns au64
instead of the largerDataValue
- this affects
LowerCtx::get_input
, which returns aLowerInput
with aconstant: Option<u64>
field--this field could go away (12 uses), replaced byctx.get_immediate(...)
To keep ConstantPool's de-duplicated values AND allow const_addr to retrieve the address of a constant:
- [x]
MachBuffer::defer_constant
should keep a mapping of constant values toMachLabels
(e.g. aHashMap
?) and only return theMachLabel
for a given value, de-duplicating them- this is redundant work since
ConstantPool
has already done this, but it is necessary work because lowering could introduce new constant values- I struggle with how to approach this inefficiency (suggestions?): this approach would mean that entire values would have to hashed/compared every time they are used (unlike
ConstantPool
, which usesConstant(u32)
handles)... but perhaps this could be optimized in the future- perhaps we can even genericize the ConstantPool and reuse it?
When the old backend goes away:
ConstantPool
can be simplified:ConstantPool::get_offset
,ConstantPool::set_offset
,binemit::const_disp4
can all go away since they are only necessary to work around old backend quirks.
abrown edited a comment on Issue #1385:
@cfallin, now that I think I understand the new backend better, I think this issue can be moved a bit further:
How things currently work in the new backend:
- at the emission level,
MachBuffer::pending_constants
is the vector containing the constant values; eventually these get emitted into the buffer and theMachLabel
s are fixed upMachBuffer::defer_constant(label, align, data, max_distance)
is the user-accessible way to add a constant during emission (e.g. see uses ofsink.defer_constant...
inemit.rs
)Inst::gen_constant
is the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64
in sizeso there are problems:
Inst::gen_constant
cannot be used to generate vector valuesMachBuffer::defer_constant
will duplicate constant valuesconst_addr
(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabel
to use inMachBuffer
; to implement this instruction, we would have to search throughMachBuffer::pending_constants
to find a value that matched theConstantData
behind itsconst_addr
'sConstant
handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constant
to accept aDataValue
instead of au64
(involves fixing up 9 uses of this method)- [ ] search through
lower.rs
to see other places that should be usinggen_constant
but are not (e.g. usingPXOR
directly)- [x] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD
) and all 1s (CMPPS/PCMPEQ
)- [x] modify
Inst::XmmLoadConstSeq
to becomeXmmLoadConst
--instead of emitting the vector inline with the function code it would usesink.defer_constant
all of this suggests some related refactoring:
- remove
LowerCtx::get_constant
:LowerCtx::get_constant
andLowerCtx::get_immediate
are doing the same thing except thatget_constant
returns au64
instead of the largerDataValue
- this affects
LowerCtx::get_input
, which returns aLowerInput
with aconstant: Option<u64>
field--this field could go away (12 uses), replaced byctx.get_immediate(...)
To keep ConstantPool's de-duplicated values AND allow const_addr to retrieve the address of a constant:
- [x]
MachBuffer::defer_constant
should keep a mapping of constant values toMachLabels
(e.g. aHashMap
?) and only return theMachLabel
for a given value, de-duplicating them- this is redundant work since
ConstantPool
has already done this, but it is necessary work because lowering could introduce new constant values- I struggle with how to approach this inefficiency (suggestions?): this approach would mean that entire values would have to hashed/compared every time they are used (unlike
ConstantPool
, which usesConstant(u32)
handles)... but perhaps this could be optimized in the future- perhaps we can even genericize the ConstantPool and reuse it?
When the old backend goes away:
ConstantPool
can be simplified:ConstantPool::get_offset
,ConstantPool::set_offset
,binemit::const_disp4
can all go away since they are only necessary to work around old backend quirks.
abrown edited a comment on Issue #1385:
@cfallin, now that I think I understand the new backend better, I think this issue can be moved a bit further:
How things currently work in the new backend:
- at the emission level,
MachBuffer::pending_constants
is the vector containing the constant values; eventually these get emitted into the buffer and theMachLabel
s are fixed upMachBuffer::defer_constant(label, align, data, max_distance)
is the user-accessible way to add a constant during emission (e.g. see uses ofsink.defer_constant...
inemit.rs
)Inst::gen_constant
is the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64
in sizeso there are problems:
Inst::gen_constant
cannot be used to generate vector valuesMachBuffer::defer_constant
will duplicate constant valuesconst_addr
(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabel
to use inMachBuffer
; to implement this instruction, we would have to search throughMachBuffer::pending_constants
to find a value that matched theConstantData
behind itsconst_addr
'sConstant
handleTo simplify the creation of vector constants (all constants, really):
- [x] change the signature of
MachInst::gen_constant
to accept aDataValue
instead of au64
(involves fixing up 9 uses of this method)- [ ] search through
lower.rs
to see other places that should be usinggen_constant
but are not (e.g. usingPXOR
directly)- [x] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD
) and all 1s (CMPPS/PCMPEQ
)- [x] modify
Inst::XmmLoadConstSeq
to becomeXmmLoadConst
--instead of emitting the vector inline with the function code it would usesink.defer_constant
all of this suggests some related refactoring:
- remove
LowerCtx::get_constant
:LowerCtx::get_constant
andLowerCtx::get_immediate
are doing the same thing except thatget_constant
returns au64
instead of the largerDataValue
- this affects
LowerCtx::get_input
, which returns aLowerInput
with aconstant: Option<u64>
field--this field could go away (12 uses), replaced byctx.get_immediate(...)
To keep ConstantPool's de-duplicated values AND allow const_addr to retrieve the address of a constant:
- [x]
MachBuffer::defer_constant
should keep a mapping of constant values toMachLabels
(e.g. aHashMap
?) and only return theMachLabel
for a given value, de-duplicating them- this is redundant work since
ConstantPool
has already done this, but it is necessary work because lowering could introduce new constant values- I struggle with how to approach this inefficiency (suggestions?): this approach would mean that entire values would have to hashed/compared every time they are used (unlike
ConstantPool
, which usesConstant(u32)
handles)... but perhaps this could be optimized in the future- perhaps we can even genericize the ConstantPool and reuse it?
When the old backend goes away:
ConstantPool
can be simplified:ConstantPool::get_offset
,ConstantPool::set_offset
,binemit::const_disp4
can all go away since they are only necessary to work around old backend quirks.
Last updated: Dec 23 2024 at 12:05 UTC