前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Hard模式赛道如何破关?这种“朴素”方法也管用

Hard模式赛道如何破关?这种“朴素”方法也管用

原创
作者头像
腾讯极客挑战赛
修改2021-06-17 11:54:54
5180
修改2021-06-17 11:54:54
举报
文章被收录于专栏:极客挑战赛极客挑战赛

在第二期极客挑战赛的MIPS64赛道中,“我就看看不参加”同学以581字节的成绩最终获得赛道冠军。除了是赛道第一名,他还是所有赛道中累计提交次数最多的同学(共85次)。一次次的提交,一次次的改进优化,一个个字节的减少,是锲而不舍、不断打磨的精神体现,也是追求技术极致的乐趣所在。

下面由他带来自己的解题思路和心得分享,也欢迎小伙伴们在文末留言,分享自己的解题报告链接。(原赛题传送门:腾讯极客挑战赛丨全世界最最最小的程序,等你来battle!


ELF file header 中间有一些空,可以插入一些常数和代码,后面的8字节可以直接去掉。去掉后末尾的phentsize为1,刚好Program segment header的p_type也为1,所以可以共用2字节。

Program segment header后的align可以直接去掉,中间的空以及p_filesz,p_memsz的低位,可以插入一些常数和代码。

具体处理如下:

代码语言:javascript
复制
.include "const.asm"

elfHead:
.long   0x464c457f    
.long   0x00010102    
md5Magic:
.long   0x67452301
.long   0xefcdab89

.short   2 # e_type
.short   8 # e_machine,
.long   1 # e_version 

.quad   ENTRY_ADDR # e_entry, 
.quad   0x38 #e_phoff

li   $28,LOAD_ADDR
.setnoreorder
.setnomacro
bmain
lw$11,8($28)
.setmacro
.setreorder

.short   64 #e_ehsize
.short   56 #e_phentsiz

progHead:
.long   1 #p_type
.long   7 #p_flags,
.quad   P_OFFSET # p_offset
.quad   ENTRY_ADDR # p_vaddr,
sin1:       #cos1 直接根据sin1算出
.long2399735053
.long1072360788

.long1106247680 
double4294967296: 
.long 0
.long1106247680
.long 0

...

由于MIPS指令都是定长的4字节,加之水平有限,所以中间还是有很多空间没有利用。

然后就是计算MD5,然后转换,都是很常规的做法,尽量重复利用指令。

sinx的计算

MIPS64貌似没有直接计算sin的指令,这里可以采用泰勒展开式:

迭代到17就可满足精度,但是我用泰勒公式写出来的指令较多

因为不需要计算任意角,只需要计算1-64的整数,所以最后采用了两角和公式:

sin1、cos1作为常数,循环依次加到64即可

代码语言:javascript
复制
...

ldc1$f2,DATA_BASE($28)   #sin1
lwc1$f1,DATA_BASE-24($28) #cos(x-1)   因为文件头中数值为1的很多,随便读一个即可
cvt.d.w$f1,$f1             #cos(x-1)   因为文件头中数值为1的很多,随便读一个即可
dmtc1$0,$f0               #sin(x-1) 貌似线上mips赛道即使为0,寄存器也要初始化
nmsub.d $f3,$f1,$f2,$f2     #计算cos1
sqrt.d $f3,$f3             #计算cos1

...


mul.d$f7,$f0,$f3     #sin(x-1)*cos1
mul.d$f0,$f0,$f2     #sin(x-1)*sin1
madd.d$f7,$f7,$f1,$f2   #sinx = cos(x-1)*sin1 + sin(x-1)*cos1 
msub.d$f1,$f0,$f1,$f3   #conx = cos(x-1)*cos1 - sin(x-1)*sin1

...

这里因为不用考虑性能,可以不用预先计算,在需要时计算,省略循环和读写的指令

#MIPS跟另两个平台的一些区别

  • 线上MIPS环境貌似寄存器即使为0,也需要初始化
  • MIPS架构中,为了充分利用CPU流水线,所有转移指令后都紧跟一条延迟槽指令。非Likely转移指令延迟槽中指令总会得到执行。所以这里需要特别注意,为了防止编译器自动用nop填充延迟槽,可以用.setnoreorder.set  nomacro,然后自己写延迟槽指令。
  • MIPS和ARM貌似没有循环左移指令(大概是因为循环左移跟循环右移可以转换),所以需要把循环左移的常数换成循环右移的常数,避免在运行时转换

一些优化点

  • 最后的syscall指令中,最后3字节可以去掉,syscall指令为0x0000000c,高位的3个字节都是0可以去掉,填充后变成了0x0000080c, 但是syscall指令第6-26bit貌似可以随意更改(syscall 0 - 0xfffff),所以两个syscall指令第6-26bit其实也可以利用

总体方法很朴素,计算md5也没做特殊处理,就是老老实实的算md5然后转换成数字或字母。结束后看群里大佬们的交流,发现跟大佬们差距还很大,才知道还有很多地方可以优化,大佬们请轻喷!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • sinx的计算
  • 一些优化点
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档