diff --git a/Research/Borland C++ decompilation.md b/Research/Borland C++ decompilation.md index c85cf952..3501d280 100644 --- a/Research/Borland C++ decompilation.md +++ b/Research/Borland C++ decompilation.md @@ -130,6 +130,47 @@ case it's part of an arithmetic expression that was promoted to `int`. // Note the opposite order of variables! ``` +* For trivially copyable structures, copy assignments are optimized to an + equivalent of `memcpy()`: + + | Structure size | (no flags) | -G | + |----------------|-------------|----------------------| + | 1 | via `AL` | via `AL` | + | 2 | via `AX` | via `AX` | + | 3 | `SCOPY@` | via `AX` and `AL` | + | 4 | via `DX:AX` | via `DX:AX` | + | 5, 7, 9 | `SCOPY@` | via `AX` and `AL` | + | 6, 8 | `SCOPY@` | via `AX` | + | 10, 12, 14, … | `SCOPY@` | `REP MOVSW` | + | 11, 13, 15, … | `SCOPY@` | `REP MOVSW`, `MOVSB` | + + (With the `-3` flag, `EAX` is used instead of `DX:AX` in the 4-byte case, + but everything else stays the same.) + + Breaking triviality by overloading `operator =` in any of the structure + members also breaks this optimization. In some cases, it might be possible + to recreate it, by simulating triviality in an overloaded copy assignment + operator inside the class in question: + + ```c++ + struct Nontrivial { + nontrivial_char_t e[100]; + // Functions containing local classes aren't expanded inline, so... + struct Trivial { + char e[100]; + }; + + void operator =(const Nontrivial &other) { + reinterpret_cast(*this) = ( + reinterpret_cast(other) + ); + } + }; + ``` + + However, this only generates identical code to the original optimization if + passing the `other` parameter can be inlined, which isn't always the case. + ## `switch` statements * Sequence of the individual cases is identical in both C and ASM diff --git a/th01/main/bullet/pellet.cpp b/th01/main/bullet/pellet.cpp index 1c37b7ae..5578dd39 100644 --- a/th01/main/bullet/pellet.cpp +++ b/th01/main/bullet/pellet.cpp @@ -82,7 +82,7 @@ bool16 pattern_velocity_set( switch(pattern) { case PP_1: ret_y.v = speed; - ret_x = 0.0f; + ret_x.set(0.0f); done = true; break; case PP_1_AIMED: @@ -179,7 +179,7 @@ inline subpixel_t base_speed_for_rank(void) #define pellet_init(pellet, left, top, pattern) \ pellet->decay_frame = 0; \ pellet->cur_left.v = TO_SP(left); \ - pellet->cur_top = top; \ + pellet->cur_top.v = TO_SP(top); \ pellet->cloud_left = left; \ pellet->cloud_top = top; \ if(spawn_with_cloud) { \ @@ -271,7 +271,7 @@ void CPellets::add_single( p->age = 0; alive_count++; p->spin_center.x.v = TO_SP(spin_center_x); - p->spin_center.y = spin_center_y; + p->spin_center.y.v = TO_SP(spin_center_y); if(motion_type == PM_SPIN) { vector2(vel_x.v, vel_y.v, speed_for_motion_fixed, angle); p->spin_velocity.x.v = vel_x.v; @@ -339,27 +339,27 @@ void CPellets::motion_type_apply_for_cur(void) || (p->cur_top.v <= to_sp(PELLET_BOUNCE_TOP_MIN)) ) { p->velocity.x.v = -p->velocity.x.v; - p->velocity.y = 0.0f; + p->velocity.y.set(0.0f); p->motion_type = PM_GRAVITY; // Nope, this doesn't help. if(p->cur_left.v <= to_sp(PELLET_LEFT_MIN)) { - p->cur_left = (PELLET_LEFT_MIN + 1.0f); + p->cur_left.set(PELLET_LEFT_MIN + 1.0f); } if(p->cur_left.v >= to_sp(PELLET_LEFT_MAX)) { - p->cur_left = (PELLET_LEFT_MAX - 1.0f); + p->cur_left.set(PELLET_LEFT_MAX - 1.0f); } if(p->cur_top.v <= to_sp(PELLET_TOP_MIN)) { - p->cur_top = (PELLET_TOP_MIN + 1.0f); + p->cur_top.set(PELLET_TOP_MIN + 1.0f); } } break; case PM_FALL_STRAIGHT_FROM_TOP_THEN_NORMAL: if(p->cur_top.to_screen() <= PELLET_BOUNCE_TOP_MIN) { - p->velocity.x = 0.0f; + p->velocity.x.set(0.0f); p->velocity.y.v = p->speed.v; p->motion_type = PM_NORMAL; if(p->cur_top.to_screen() <= PLAYFIELD_TOP) { - p->cur_top = (PLAYFIELD_TOP + 1.0f); + p->cur_top.set(PLAYFIELD_TOP + 1.0f); } } break; @@ -370,8 +370,7 @@ void CPellets::motion_type_apply_for_cur(void) p->cur_top.v = (to_spin_circle(Sin8(p->angle)) + p->spin_center.y.v); p->spin_center.x.v += p->spin_velocity.x.v; p->spin_center.y.v += p->spin_velocity.y.v; - p->velocity.x = 0.0f; - p->velocity.y = 0.0f; + p->velocity.set(0.0f, 0.0f); p->angle += PELLET_SPIN_DELTA_ANGLE; #undef to_spin_circle break; diff --git a/th01/math/subpixel.hpp b/th01/math/subpixel.hpp index f64d77e8..be5c883e 100644 --- a/th01/math/subpixel.hpp +++ b/th01/math/subpixel.hpp @@ -30,11 +30,13 @@ public: this->v -= static_cast(to_sp(screen_v)); } - void operator =(float screen_v) { + // No overloads of `operator =()`, since the class needs to be trivially + // copyable. + void set(float screen_v) { v = static_cast(to_sp(screen_v)); } - void operator =(const T &screen_v) { + void set(const T &screen_v) { v = TO_SP(screen_v); } @@ -55,13 +57,8 @@ template struct SPPointBase { T x, y; void set(float screen_x, float screen_y) { - x = screen_x; - y = screen_y; - } - - void set(const T &screen_x, const T &screen_y) { - x = screen_x; - y = screen_y; + x.set(screen_x); + y.set(screen_y); } }; diff --git a/th04/math/motion.hpp b/th04/math/motion.hpp index 61897163..c38a91fa 100644 --- a/th04/math/motion.hpp +++ b/th04/math/motion.hpp @@ -4,9 +4,9 @@ typedef struct { SPPoint velocity; void init(float screen_x, float screen_y) { - cur.x = screen_x; - prev.x = screen_x; - cur.y = screen_y; - prev.y = screen_y; + cur.x.set(screen_x); + prev.x.set(screen_x); + cur.y.set(screen_y); + prev.y.set(screen_y); } } motion_t; diff --git a/th05/main/player/shot.hpp b/th05/main/player/shot.hpp index ce538cd3..38f8db7d 100644 --- a/th05/main/player/shot.hpp +++ b/th05/main/player/shot.hpp @@ -66,8 +66,8 @@ struct ShotAddIterator { #define MISSILE_R shot->type = ST_MISSILE_RIGHT #define MISSILE_S shot->type = ST_MISSILE_STRAIGHT -#define VELOCITY_X(screen_x) shot->pos.velocity.x = screen_x; -#define VELOCITY_Y(screen_y) shot->pos.velocity.y = screen_y; +#define VELOCITY_X(screen_x) shot->pos.velocity.x.set(screen_x); +#define VELOCITY_Y(screen_y) shot->pos.velocity.y.set(screen_y); #define VELOCITY_XY(screen_x, screen_y) \ VELOCITY_X(screen_x); \ diff --git a/th05/main/stage/setup.cpp b/th05/main/stage/setup.cpp index 416ad330..88a7b23f 100644 --- a/th05/main/stage/setup.cpp +++ b/th05/main/stage/setup.cpp @@ -40,7 +40,7 @@ void pascal near stage2_setup(void) midboss_render_func = midboss2_render; midboss.frames_until = 2750; midboss.pos.init(192, -32); - midboss.pos.velocity.set(0, 0.5f); + midboss.pos.velocity.set(0.0f, 0.5f); midboss.hp = 1400; midboss.sprite = 202; @@ -67,7 +67,7 @@ void pascal near stage3_setup(void) midboss_render_func = midboss3_render; midboss.frames_until = 5750; midboss.pos.init(192, -32); - midboss.pos.velocity.set(0, 0.5f); + midboss.pos.velocity.set(0.0f, 0.5f); midboss.hp = 1400; midboss.sprite = 208; @@ -97,7 +97,7 @@ void pascal near stage4_setup(void) midboss_render_func = midboss4_render; midboss.frames_until = 3900; midboss.pos.init(192, -32); - midboss.pos.velocity.set(0, 0.5f); + midboss.pos.velocity.set(0.0f, 0.5f); midboss.hp = 1100; midboss.sprite = 208; @@ -126,7 +126,7 @@ void pascal near stage5_setup(void) midboss_render_func = midboss5_render; midboss.frames_until = 4800; midboss.pos.init(192, -32); - midboss.pos.velocity.set(0, 0.5f); + midboss.pos.velocity.set(0.0f, 0.5f); midboss.hp = 1550; midboss.sprite = 212; diff --git a/th05/p_mima.cpp b/th05/p_mima.cpp index b5d2459d..fd8369c8 100644 --- a/th05/p_mima.cpp +++ b/th05/p_mima.cpp @@ -18,7 +18,7 @@ void pascal near shot_mima_l2(void) if(sai.i <= 2) { if(sai.i == 2) { shot->from_option_l(); } else/*i == 1*/ { shot->from_option_r(); } - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); shot->set_option_sprite_and_damage(5); } else { shot->set_random_angle_forwards(); @@ -37,12 +37,12 @@ void pascal near shot_mima_l3(void) if(sai.i <= 2) { if(sai.i == 2) { shot->from_option_l(); } else/*i == 1*/ { shot->from_option_r(); } - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); shot->set_option_sprite_and_damage(5); } else { if(sai.i == 4) { shot->pos.cur.x -= 8.0f; } else/*i == 3*/ { shot->pos.cur.x += 8.0f; } - shot->pos.velocity.y = -12.0f; + shot->pos.velocity.y.set(-12.0f); shot->damage = 7; } if(sai.next() <= 0) { @@ -63,12 +63,12 @@ void pascal near shot_mima_l4(void) case 3: shot->from_option_l(); break; case 2: shot->from_option_r(); break; } - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); shot->set_option_sprite_and_damage(4); } else { if(sai.i == 6) { shot->pos.cur.x -= 8.0f; } else/* i== 5*/ { shot->pos.cur.x += 8.0f; } - shot->pos.velocity.y = -12.0f; + shot->pos.velocity.y.set(-12.0f); shot->damage = 7; } if(sai.next() <= 0) { @@ -90,7 +90,7 @@ void pascal near shot_mima_l5(void) case 1: VELOCITY_X(-1.0f); shot->from_option_r(); break; case 2: shot->from_option_r(); break; } - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); shot->set_option_sprite_and_damage(4); } else { shot_velocity_set(&shot->pos.velocity, sai.angle); @@ -115,7 +115,7 @@ void pascal near shot_mima_l6(void) case 2: VELOCITY_X(-0.5f); shot->from_option_r(); break; case 3: VELOCITY_X( 0.5f); shot->from_option_l(); break; } - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); shot->set_option_sprite_and_damage(4); } else { shot_velocity_set(&shot->pos.velocity, sai.angle); @@ -143,7 +143,7 @@ void pascal near shot_mima_l7(void) case 3: VELOCITY_X( 0.5f); shot->from_option_l(); break; case 5: shot->from_option_l(); break; } - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); shot->set_option_sprite_and_damage(4); } else { shot_velocity_set(&shot->pos.velocity, sai.angle); @@ -162,7 +162,7 @@ void pascal near shot_mima_l8(void) sai.angle = 186; while(( shot = shots_add() ) != NULL) { if(sai.i <= 6) { - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); switch(sai.i - 1) { // Beware, non-sequential case order! case 0: VELOCITY_XY( 1.75f, -18.0f); shot->from_option_r(8.0f); break; @@ -172,7 +172,7 @@ void pascal near shot_mima_l8(void) case 3: VELOCITY_X ( 0.5f); shot->from_option_l(); break; case 5: shot->from_option_l(); break; } - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); shot->set_option_sprite_and_damage(4); } else { shot_velocity_set(&shot->pos.velocity, sai.angle); @@ -190,7 +190,7 @@ void pascal near shot_mima_l9(void) SHOT_FUNC_INIT(6, SC_6X, SC_3X, i += 4); while(( shot = shots_add() ) != NULL) { if(sai.i <= 6) { - shot->pos.velocity.y = -20.0f; + shot->pos.velocity.y.set(-20.0f); switch(sai.i - 1) { case 0: VELOCITY_XY( 2.625f, -18.0f); shot->from_option_r(8.0f); break; case 1: VELOCITY_XY(-2.625f, -18.0f); shot->from_option_l(8.0f); break; diff --git a/th05/p_yuuka.cpp b/th05/p_yuuka.cpp index 1a19a87c..21aeb507 100644 --- a/th05/p_yuuka.cpp +++ b/th05/p_yuuka.cpp @@ -102,7 +102,7 @@ void pascal near shot_yuuka_l4(void) sai.i = 1; } } - shot->pos.velocity.y = 2.0f; + shot->pos.velocity.y.set(2.0f); shot->set_option_sprite_and_damage(6); } shot_velocity_set(&shot->pos.velocity, sai.angle);