Я пытаюсь добавить некоторые обобщения в проект C ++, поэтому я использую std :: tuple для хранения полей.
Я исследую разницу в коде ассемблера между std :: get <1> (myClass. myTuple) .read () и myClass.myField.read (). Я использую ARM GNU Toolchain 9.2 и процессор ARM-M4F.
myField.read () скомпилирован как:
9A02 ldr r2, [r13, #8]
E71F b #0x16a0
, что очевидно и ожидаемо.
Хотя std :: get <1> (myTuple) .read () компилируется следующим образом:
9B82 ldr r3, [r13, #0x208]
695B ldr r3, [r3, #0x14]
42AB cmp r3, r5
D11B bne #0x1a3c
F8BD0210 ldrh.w r0, [r13, #0x210]
E761 b #0x1792
Теоретически std :: get должен оцениваться во время компиляции, и результат должен быть одинаковым в обоих случаях .
По какой причине мы получаем это дополнительное сравнение?
9B82 ldr r3, [r13, #0x208]
695B ldr r3, [r3, #0x14]
42AB cmp r3, r5
D11B bne #0x1a3c
Я не могу найти ситуацию, когда код следует за этой ветвью.
Что я могу сделать, чтобы упростить этот код?
Могу ли я что-то изменить в своем коде C ++, чтобы получить результат, эквивалентный прямому доступу к полю (myField.read ())?
Как заставить компилятор не генерировать это дополнительный кусок кода?
Любая помощь приветствуется.
Ниже я приложил минимальный воспроизводимый пример.
class Base {
public:
virtual uint32_t read1() const = 0;
virtual void write1(const uint32_t n) = 0;
};
class Neighbor : public Base {
public:
uint32_t read_val1 = 1;
uint32_t read1() const {
return read_val1;
}
void write1(const uint32_t n) {
read_val1 = n;
}
};
struct Abc1 {
std::tuple<Neighbor, Neighbor, Neighbor, Neighbor, Neighbor, Neighbor, Neighbor, Neighbor, Neighbor, Neighbor> att;
Abc1() : att (
Neighbor(),
Neighbor(),
Neighbor(),
Neighbor(),
Neighbor(),
Neighbor(),
Neighbor(),
Neighbor(),
Neighbor(),
Neighbor()
) {}
uint32_t read1(uint32_t index) {
switch(index){
case 0:
return std::get < 0 > (att).read1();
case 1:
return std::get < 1 > (att).read1();
case 2:
return std::get < 2 > (att).read1();
case 3:
return std::get < 3 > (att).read1();
case 4:
return std::get < 4 > (att).read1();
case 5:
return std::get < 5 > (att).read1();
case 6:
return std::get < 6 > (att).read1();
case 7:
return std::get < 7 > (att).read1();
case 8:
return std::get < 8 > (att).read1();
case 9:
return std::get < 9 > (att).read1();
}
return 0;
}
void write1(uint32_t index, uint32_t n) {
switch(index){
case 0:
std::get < 0 > (att).write1(n);
break;
case 1:
std::get < 1 > (att).write1(n);
break;
case 2:
std::get < 2 > (att).write1(n);
break;
case 3:
std::get < 3 > (att).write1(n);
break;
case 4:
std::get < 4 > (att).write1(n);
break;
case 5:
std::get < 5 > (att).write1(n);
break;
case 6:
std::get < 6 > (att).write1(n);
break;
case 7:
std::get < 7 > (att).write1(n);
break;
case 8:
std::get < 8 > (att).write1(n);
break;
case 9:
std::get < 9 > (att).write1(n);
break;
}
return;
}
};
int main(){
volatile uint32_t no = 0;
for(volatile int j = 0; j<10000; j++){
no += abc1.read1(j%10);
abc1.write1(j%10, no);
}
}
/ Адам