for循环产生的Cortex-M3汇编代码的一个奇怪现象
最近比较一下KEIL和IAR两个编译器产生的代码,基于Cortex-M3处理器的,然后发现了一几个奇怪的地方。
很简单的一个C的for循环
void fun_for_add_65535(void)
{
int i;
for (i=; i<65535; i++)
;
} void fun_for_add_65536(void)
{
int i;
for (i=; i<65536; i++)
;
}
按道理这两个函数除了for的终止值不同之外,产生的汇编代码应该不会有什么差异。但是不是。
先看一下在不优化的情况下产生的 void fun_for_add_65535(void) 汇编代码,IAR不优化是-On选项。
使用fromelf打印出汇编语句如下:
void fun_for_add_65535(void)
{
int i;
for (i=; i<65535; i++)
\ fun_for_add_65535:
\ MOVS R1,#+
\ MOVS R0,R1
\ ??fun_for_add_65535_0:
\ 4FF6FF71 MOVW R1,#+
\ CMP R0,R1
\ 0000000A 01DA BGE.N ??fun_for_add_65535_1
\ 0000000C 401C ADDS R0,R0,#+
\ 0000000E F9E7 B.N ??fun_for_add_65535_0
;
}
\ ??fun_for_add_65535_1:
\ BX LR ;; return
再来看一下不优化的情况下产生的 void fun_for_add_65536(void) 汇编代码,如下:
首先说明一个是产生的汇编代码中没有进入函数和退出函数时对寄存器的入栈和出栈的封皮,因为没有使用到R4以上的寄存器。
根据ARM 过程调用标准(简称APCS)。
一般来说cortex-m3在编译时会用R0-R3最参数传递,而多余4个的参数需要用到堆栈,返回时返回值放在R0,也就是APCS里约定:每个函数假定R0-R3在函数调用后会改变,而其他的通用寄存器除SP,LR,PC的不会改变,也就是你如果在函数里使用R4,R5等,你要对其进行压栈操作,函数返回时要恢复。因为调用你得函数认为R4,R5它调用完你之后不会改变。
void fun_for_add_65536(void)
{
int i;
for (i=; i<65536; i++)
\ fun_for_add_65536:
\ MOVS R1,#+
\ MOVS R0,R1
\ ??fun_for_add_65536_0:
\ B0F5803F CMP R0,#+
\ 01DA BGE.N ??fun_for_add_65536_1
\ 0000000A 401C ADDS R0,R0,#+
\ 0000000C FAE7 B.N ??fun_for_add_65536_0
;
}
\ ??fun_for_add_65536_1:
\ 0000000E BX LR ;; return
抛开一些无关信息,直接对比两个汇编文件的关键信息:
fun_for_add_65535: |
fun_for_add_65536: |
我把主要的差异点标了出来,很奇怪,为什么65535(0xFFFF)反而要比65536(0x10000)多了一条MOVW的指令,而65536的CMP指令可以直接比较呢?
65535的CMP只有一条THUMB2(8842),应该是因为直接寄存器比较,而65536的则是和立即数比较,所以产生的是B0F5803F 。
总的来说65536的for循环多了两个byte的代码,但是执行时间上快了,因为少了每次比较前的对R1赋值的操作。
IAR产生了这样的代码,看看KEIL产生的代码是怎么样的。
这是 fun_for_add_65535 产生的
;;;7 //char *str = "abcdefg";
;;;8 void fun_for_add_65535(void)
MOVS r0,#
;;;9 {
;;;10 int i;
;;;11 for (i=0; i<65535; i++)
e000 B |L1.|
|L1.|
1c40 ADDS r0,r0,#
|L1.|
f64f71ff MOV r1,#0xffff
00000a CMP r0,r1
00000c dbfa BLT |L1.|
;;;12 ;
;;;13 }
00000e BX lr
;;;14
ENDP
这是 fun_for_add_65536产生的,
fun_for_add_65536 PROC
;;;15 void fun_for_add_65536(void)
MOVS r0,#
;;;16 {
;;;17 int i;
;;;18 for (i=0; i<65536; i++)
e000 B |L1.|
|L1.|
1c40 ADDS r0,r0,#
|L1.|
f5b03f80 CMP r0,#0x10000
00001a dbfb BLT |L1.|
;;;19 ;
;;;20 }
00001c BX lr
;;;21
ENDP
同样把主要的差异点标了出来,同样的现象,65536的多了一行MOV赋值给R1的汇编代码
6 ;;;11 for (i=0; i<65535; i++) |
6 ;;;18 for (i=0; i<65536; i++) |
尝试开了一下优化,在IAR low优化级别下,产生的汇编代码差异还是一样,65535多了一行赋值代码。
5 \ fun_for_add_65535: |
5 \ fun_for_add_65536: |
在IAR medium的优化级别下,汇编实现则变了,但总的来说还是65536的实现更加快速一些。
而在KEIL开O1级优化下,两个代码除了初值不同没什么差异。如下所示:
1 ;;;11 for (i=0; i<65535; i++) |
5 ;;;18 for (i=0; i<65536; i++) |
优化之后处理是要好了很多。
甚是奇怪,是编译器的问题吗?继续尝试其他的值,发现大于65535的其他值产生的代码都一样,除了初值不同。然而小于65535的则是截然不同的,在大于4096的循环下产生的汇编代码跟65535的一样,多一条MOVW的指令,小于4096的则和65536的一样,直接CMP比较立即数。然而更加令我惊讶的是,4096的初始值和4097的初始值产生的汇编代码竟然是一样的!如下所示:
4 for (i=0; i<4096; i++) |
21 for (i=0; i<4097; i++) |
这真是让我无比惊讶,在KEIL上面4096和4097也产生了一样的代码,百思不得其解。什么问题?
最后直接在开发板上调试发现循环次数都是正确的。
猛然发现最后一条指令分别是BLT和BLE,查找arm指令手册发现LT是带符号数小于跳转,而LE则是带符号数小于等于跳转,因为4097是多一次比较的。
哈哈,想着不太可能arm编译器出现这样低级的问题的。
那么最后只剩下一个疑问就是上面的多一条MOVW指令的问题。
我的KEIL是4.73版本,IAR是5.3版本。
KEIL的armcc是ARM C/C++ Compiler, 5.03 [Build 76] [MDK-ARM Standard]
IAR的iccarm是IAR ANSI C/C++ Compiler V5.30.2.51295/W32 for ARM
for循环产生的Cortex-M3汇编代码的一个奇怪现象的更多相关文章
- ARM Cortex M3(V7-M架构)硬件启动程序 一
Cortex-m3启动代码分析笔记 启动代码文件名是STM32F10X.S,它的作用先总结下,然后再分析. 启动代码作用一般是: 1)堆和栈的初始化: 2)中断向量表定义: 3)地址重映射及中断向量表 ...
- STM32学习之路入门篇之指令集及cortex——m3的存储系统
STM32学习之路入门篇之指令集及cortex——m3的存储系统 一.汇编语言基础 一).汇编语言:基本语法 1.汇编指令最典型的书写模式: 标号 操作码 操作数1, 操作数2,... ...
- cortex m0启动代码详解
转自:http://www.cnblogs.com/mddblog/p/4920063.html 阅读目录 概述 1.堆栈空间定义 2.存放中断向量表 3. 复位中断函数(Reset_Handler) ...
- 浅析VS2010反汇编 VS 反汇编方法及常用汇编指令介绍 VS2015使用技巧 调试-反汇编 查看C语言代码对应的汇编代码
浅析VS2010反汇编 2015年07月25日 21:53:11 阅读数:4374 第一篇 1. 如何进行反汇编 在调试的环境下,我们可以很方便地通过反汇编窗口查看程序生成的反汇编信息.如下图所示. ...
- STM8S汇编代码分析
转载:http://blog.csdn.net/u010093140/article/details/50021897使用STVD建立完汇编工程项目之后(具本建立方法可以看我的另一篇博文http:// ...
- ARM汇编初探---汇编代码中都有哪几类指令---ARM伪指令介绍
要学习一个东西首先要把概念搞清楚,以下仅仅是自己的一些关于汇编的理解. 可运行文件里的01码是机器码,机器码不等于汇编码,尽管机器码能够非常easy翻译成汇编码. 汇编码中包括非常多汇编指令.伪指令和 ...
- 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
[嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 ) 一. 内存 ...
- 【freertos】002-posix模拟器设计与cortex m3异常处理
目录 前言 posix 标准接口层设计 模拟器的系统心跳 模拟器的task底层实质 模拟器的任务切换原理 cortex M3/M4异常处理 双堆栈指针 双操作模式 栈帧 EXC_RETURN 前言 如 ...
- linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作
一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...
随机推荐
- 浅谈C++中指针和引用的区别
指针和引用在C++中很常用,但是对于它们之间的区别很多初学者都不是太熟悉,下面来谈谈他们2者之间的区别和用法. 1.指针和引用的定义和性质区别: (1)指针:指针是一个变量,只不过这个变量存储的是一个 ...
- hive 配置MySQL库
chkconfig mysqld on MySQL开机自启动 建库: --hive数据库2create database hive DEFAULT CHARSET utf8 COLLATE utf8_ ...
- ResultSet与Result
微软的.NET平台上面的数据访问有一个特点,就是数据查询的结果,可以放在内存中,以XML格式进行描述,不需要一直与数据库保持在线连接,用DataSet + Data Adapter来实现! 而在JDB ...
- DevExpress中GridControl的属性设置
1.隐藏最上面的GroupPanel gridView1.OptionsView.ShowGroupPanel=false; 2.得到当前选定记录某字段的值 sValue=Table.Rows[gri ...
- Could not lock surface java.lang.IllegalArgumentException
08-07 14:46:33.795: E/Surface(4927): dequeueBuffer failed (Invalid argument) 08-07 14:46:33.800: E/V ...
- js-计算器
<div class="main"><h1>HTML5-计算器</h1> <input id="num1& ...
- QQ在线咨询状态显示不出来怎么办?http://bizapp.qq.com/webpres.htm
- XDocument读取xml的所有元素以及XPath语法
原文 http://www.cnblogs.com/xxyishutong/p/3326375.html <?xml version="1.0" encoding=&quo ...
- Ubuntu 下开发环境的常规配置。
Install Chinese input (for Chinese peers) We Choose the Sougou pinying.(搜狗) $ sudo apt-get install g ...
- UNIX网络编程卷1 时间获取程序server UDP 协议无关
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie /** * UDP 协议无关 调用 getaddrinfo 和 udp_server **/ ...