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_addrand 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
ConstantPoolimplementation 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_addrand 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
ConstantPoolimplementation 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_constantsis the vector containing the constant values; eventually these get emitted into the buffer and theMachLabels 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_constantis the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64in sizeso there are problems:
Inst::gen_constantcannot be used to generate vector valuesMachBuffer::defer_constantwill duplicate constant valuesconst_addr(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabelto use inMachBuffer; to implement this instruction, we would have to search throughMachBuffer::pending_constantsto find a value that matched theConstantDatabehind itsconst_addr'sConstant` handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constantto accept aDataValueinstead of au64(involves fixing up 9 uses of this method)- [ ] search through
lower.rsto see other places that should be usinggen_constantbut are not (e.g. usingPXORdirectly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD) and all 1s (CMPPS/PCMPEQ)- [ ] modify
Inst::XmmLoadConstSeqto becomeXmmLoadConst--instead of emitting the vector inline with the function code it would usesink.defer_constantall of this suggests some related refactoring:
- remove
LowerCtx::get_constant:LowerCtx::get_constantandLowerCtx::get_immediateare doing the same thing except thatget_constantreturns au64instead of the largerDataValue- this affects
LowerCtx::get_input, which returns aLowerInputwith 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_constantshould keep a mapping of constant values toMachLabels(e.g. aHashMap?) and only return theMachLabelfor a given value, de-duplicating them- this is redundant work since
ConstantPoolhas 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:
ConstantPoolcan be simplified:ConstantPool::get_offset,ConstantPool::set_offset,binemit::const_disp4can 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_constantsis the vector containing the constant values; eventually these get emitted into the buffer and theMachLabels 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_constantis the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64in sizeso there are problems:
Inst::gen_constantcannot be used to generate vector valuesMachBuffer::defer_constantwill duplicate constant valuesconst_addr(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabelto use inMachBuffer; to implement this instruction, we would have to search throughMachBuffer::pending_constantsto find a value that matched theConstantDatabehind itsconst_addr'sConstant` handleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constantto accept aDataValueinstead of au64(involves fixing up 9 uses of this method)- [ ] search through
lower.rsto see other places that should be usinggen_constantbut are not (e.g. usingPXORdirectly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD) and all 1s (CMPPS/PCMPEQ)- [ ] modify
Inst::XmmLoadConstSeqto becomeXmmLoadConst--instead of emitting the vector inline with the function code it would usesink.defer_constantall of this suggests some related refactoring:
- remove
LowerCtx::get_constant:LowerCtx::get_constantandLowerCtx::get_immediateare doing the same thing except thatget_constantreturns au64instead of the largerDataValue- this affects
LowerCtx::get_input, which returns aLowerInputwith 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_constantshould keep a mapping of constant values toMachLabels(e.g. aHashMap?) and only return theMachLabelfor a given value, de-duplicating them- this is redundant work since
ConstantPoolhas 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:
ConstantPoolcan be simplified:ConstantPool::get_offset,ConstantPool::set_offset,binemit::const_disp4can 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_constantsis the vector containing the constant values; eventually these get emitted into the buffer and theMachLabels 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_constantis the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64in sizeso there are problems:
Inst::gen_constantcannot be used to generate vector valuesMachBuffer::defer_constantwill duplicate constant valuesconst_addr(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabelto use inMachBuffer; to implement this instruction, we would have to search throughMachBuffer::pending_constantsto find a value that matched theConstantDatabehind itsconst_addr'sConstanthandleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constantto accept aDataValueinstead of au64(involves fixing up 9 uses of this method)- [ ] search through
lower.rsto see other places that should be usinggen_constantbut are not (e.g. usingPXORdirectly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD) and all 1s (CMPPS/PCMPEQ)- [ ] modify
Inst::XmmLoadConstSeqto becomeXmmLoadConst--instead of emitting the vector inline with the function code it would usesink.defer_constantall of this suggests some related refactoring:
- remove
LowerCtx::get_constant:LowerCtx::get_constantandLowerCtx::get_immediateare doing the same thing except thatget_constantreturns au64instead of the largerDataValue- this affects
LowerCtx::get_input, which returns aLowerInputwith 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_constantshould keep a mapping of constant values toMachLabels(e.g. aHashMap?) and only return theMachLabelfor a given value, de-duplicating them- this is redundant work since
ConstantPoolhas 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:
ConstantPoolcan be simplified:ConstantPool::get_offset,ConstantPool::set_offset,binemit::const_disp4can 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_constantis 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 toMachLabelis 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::MachInstandLowerCtx, as you suggest- Add a table to the
LowerCtxtracking labels for every constant, emitting on first request. Then we have something likeLowerCtx::constant_pool_label(...).- Eventually, deduplicate inside
LowerCtxif 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_constantsis the vector containing the constant values; eventually these get emitted into the buffer and theMachLabels 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_constantis the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64in sizeso there are problems:
Inst::gen_constantcannot be used to generate vector valuesMachBuffer::defer_constantwill duplicate constant valuesconst_addr(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabelto use inMachBuffer; to implement this instruction, we would have to search throughMachBuffer::pending_constantsto find a value that matched theConstantDatabehind itsconst_addr'sConstanthandleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constantto accept aDataValueinstead of au64(involves fixing up 9 uses of this method)- [ ] search through
lower.rsto see other places that should be usinggen_constantbut are not (e.g. usingPXORdirectly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD) and all 1s (CMPPS/PCMPEQ)- [ ] modify
Inst::XmmLoadConstSeqto becomeXmmLoadConst--instead of emitting the vector inline with the function code it would usesink.defer_constantall of this suggests some related refactoring:
- remove
LowerCtx::get_constant:LowerCtx::get_constantandLowerCtx::get_immediateare doing the same thing except thatget_constantreturns au64instead of the largerDataValue- this affects
LowerCtx::get_input, which returns aLowerInputwith 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_constantshould keep a mapping of constant values toMachLabels(e.g. aHashMap?) and only return theMachLabelfor a given value, de-duplicating them- this is redundant work since
ConstantPoolhas 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:
ConstantPoolcan be simplified:ConstantPool::get_offset,ConstantPool::set_offset,binemit::const_disp4can 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_constantsis the vector containing the constant values; eventually these get emitted into the buffer and theMachLabels 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_constantis the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64in sizeso there are problems:
Inst::gen_constantcannot be used to generate vector valuesMachBuffer::defer_constantwill duplicate constant valuesconst_addr(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabelto use inMachBuffer; to implement this instruction, we would have to search throughMachBuffer::pending_constantsto find a value that matched theConstantDatabehind itsconst_addr'sConstanthandleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constantto accept aDataValueinstead of au64(involves fixing up 9 uses of this method)- [ ] search through
lower.rsto see other places that should be usinggen_constantbut are not (e.g. usingPXORdirectly)- [ ] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD) and all 1s (CMPPS/PCMPEQ)- [x] modify
Inst::XmmLoadConstSeqto becomeXmmLoadConst--instead of emitting the vector inline with the function code it would usesink.defer_constantall of this suggests some related refactoring:
- remove
LowerCtx::get_constant:LowerCtx::get_constantandLowerCtx::get_immediateare doing the same thing except thatget_constantreturns au64instead of the largerDataValue- this affects
LowerCtx::get_input, which returns aLowerInputwith 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_constantshould keep a mapping of constant values toMachLabels(e.g. aHashMap?) and only return theMachLabelfor a given value, de-duplicating them- this is redundant work since
ConstantPoolhas 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:
ConstantPoolcan be simplified:ConstantPool::get_offset,ConstantPool::set_offset,binemit::const_disp4can 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_constantsis the vector containing the constant values; eventually these get emitted into the buffer and theMachLabels 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_constantis the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64in sizeso there are problems:
Inst::gen_constantcannot be used to generate vector valuesMachBuffer::defer_constantwill duplicate constant valuesconst_addr(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabelto use inMachBuffer; to implement this instruction, we would have to search throughMachBuffer::pending_constantsto find a value that matched theConstantDatabehind itsconst_addr'sConstanthandleTo simplify the creation of vector constants (all constants, really):
- [ ] change the signature of
MachInst::gen_constantto accept aDataValueinstead of au64(involves fixing up 9 uses of this method)- [ ] search through
lower.rsto see other places that should be usinggen_constantbut are not (e.g. usingPXORdirectly)- [x] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD) and all 1s (CMPPS/PCMPEQ)- [x] modify
Inst::XmmLoadConstSeqto becomeXmmLoadConst--instead of emitting the vector inline with the function code it would usesink.defer_constantall of this suggests some related refactoring:
- remove
LowerCtx::get_constant:LowerCtx::get_constantandLowerCtx::get_immediateare doing the same thing except thatget_constantreturns au64instead of the largerDataValue- this affects
LowerCtx::get_input, which returns aLowerInputwith 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_constantshould keep a mapping of constant values toMachLabels(e.g. aHashMap?) and only return theMachLabelfor a given value, de-duplicating them- this is redundant work since
ConstantPoolhas 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:
ConstantPoolcan be simplified:ConstantPool::get_offset,ConstantPool::set_offset,binemit::const_disp4can 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_constantsis the vector containing the constant values; eventually these get emitted into the buffer and theMachLabels 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_constantis the user-accessible way to create constant-generating instructions during lowering, but it only accepts values up tou64in sizeso there are problems:
Inst::gen_constantcannot be used to generate vector valuesMachBuffer::defer_constantwill duplicate constant valuesconst_addr(a way to retrieve the address of constants for read-only table lookups) does not know whichMachLabelto use inMachBuffer; to implement this instruction, we would have to search throughMachBuffer::pending_constantsto find a value that matched theConstantDatabehind itsconst_addr'sConstanthandleTo simplify the creation of vector constants (all constants, really):
- [x] change the signature of
MachInst::gen_constantto accept aDataValueinstead of au64(involves fixing up 9 uses of this method)- [ ] search through
lower.rsto see other places that should be usinggen_constantbut are not (e.g. usingPXORdirectly)- [x] add a way for gen_constant to generate vectors of all 0s (
PXOR/XORPS/XORPD) and all 1s (CMPPS/PCMPEQ)- [x] modify
Inst::XmmLoadConstSeqto becomeXmmLoadConst--instead of emitting the vector inline with the function code it would usesink.defer_constantall of this suggests some related refactoring:
- remove
LowerCtx::get_constant:LowerCtx::get_constantandLowerCtx::get_immediateare doing the same thing except thatget_constantreturns au64instead of the largerDataValue- this affects
LowerCtx::get_input, which returns aLowerInputwith 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_constantshould keep a mapping of constant values toMachLabels(e.g. aHashMap?) and only return theMachLabelfor a given value, de-duplicating them- this is redundant work since
ConstantPoolhas 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:
ConstantPoolcan be simplified:ConstantPool::get_offset,ConstantPool::set_offset,binemit::const_disp4can all go away since they are only necessary to work around old backend quirks.
Last updated: Dec 13 2025 at 19:03 UTC