最近比较一下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:
0021 MOVS R1,#+0
0800 MOVS R0,R1
??fun_for_add_65535_0:
4FF6FF71 MOVW R1,#+65535
8842 CMP R0,R1
01DA BGE.N ??fun_for_add_65535_1
401C ADDS R0,R0,#+1
F9E7 B.N ??fun_for_add_65535_0
      fun_for_add_65536:
0021 MOVS R1,#+0
0800 MOVS R0,R1
??fun_for_add_65536_0:
B0F5803F CMP R0,#+65536

01DA BGE.N ??fun_for_add_65536_1
401C ADDS R0,R0,#+1
FAE7 B.N ??fun_for_add_65536_0

我把主要的差异点标了出来,很奇怪,为什么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++)
7 000002 e000 B |L1.6|
8 |L1.4|
9 000004 1c40 ADDS r0,r0,#1
10 |L1.6|
11 000006 f64f71ff MOV r1,#0xffff
12 00000a 4288 CMP r0,r1
13 00000c dbfa BLT |L1.4|
14 ;;;12 ;
 6 ;;;18       for (i=0; i<65536; i++)
7 000012 e000 B |L1.22|
8 |L1.20|
9 000014 1c40 ADDS r0,r0,#1
10 |L1.22|

11 000016 f5b03f80 CMP
r0,#0x10000
12 00001a dbfb BLT |L1.20|
13 ;;;19 ;

尝试开了一下优化,在IAR low优化级别下,产生的汇编代码差异还是一样,65535多了一行赋值代码。

 5    \              fun_for_add_65535:
6 \ 0020 MOVS R0,#+0
7 \ 00E0 B.N ??fun_for_add_65535_0
8 \ ??fun_for_add_65535_1:
9 \ 401C ADDS R0,R0,#+1
10 \ ??fun_for_add_65535_0:
11 \ 4FF6FF71 MOVW R1,#+65535
12 \ 8842 CMP R0,R1
13 \ FADB BLT.N ??fun_for_add_65535_1
 5    \                fun_for_add_65536:
6 \ 0020 MOVS R0,#+0
7 \ 00E0 B.N ??fun_for_add_65536_0
8 \ ??fun_for_add_65536_1:
9 \ 401C ADDS R0,R0,#+1
10 \ ??fun_for_add_65536_0:
11 \ B0F5803F CMP R0,#+65536
12 \ FBDB BLT.N ??fun_for_add_65536_1

在IAR medium的优化级别下,汇编实现则变了,但总的来说还是65536的实现更加快速一些。

而在KEIL开O1级优化下,两个代码除了初值不同没什么差异。如下所示:

1 ;;;11       for (i=0; i<65535; i++)
2 000002 f64f71ff MOV r1,#0xffff
3 |L1.6|
4 000006 1c40 ADDS r0,r0,#1
5 000008 4288 CMP r0,r1
6 00000a dbfc BLT |L1.6|
 5 ;;;18       for (i=0; i<65536; i++)
6 000010 f44f3180 MOV r1,#0x10000
7 |L1.20|
8 000014 1c40 ADDS r0,r0,#1
9 000016 4288 CMP r0,r1
10 000018 dbfc BLT |L1.20|

优化之后处理是要好了很多。

甚是奇怪,是编译器的问题吗?继续尝试其他的值,发现大于65535的其他值产生的代码都一样,除了初值不同。然而小于65535的则是截然不同的,在大于4096的循环下产生的汇编代码跟65535的一样,多一条MOVW的指令,小于4096的则和65536的一样,直接CMP比较立即数。然而更加令我惊讶的是,4096的初始值和4097的初始值产生的汇编代码竟然是一样的!如下所示:

 4         for (i=0; i<4096; i++)
5 \ fun_for_add_4096:
6 \ 0020 MOVS R0,#+0
7 \ 00E0 B.N ??fun_for_add_4096_0
8 \ ??fun_for_add_4096_1:
9 \ 401C ADDS R0,R0,#+1
10 \ ??fun_for_add_4096_0:
11 \ B0F5805F CMP R0,#+4096
12 \ FBDB BLT.N ??fun_for_add_4096_1
21           for (i=0; i<4097; i++)
22 \ fun_for_add_4097:
23 \ 0020 MOVS R0,#+0
24 \ 00E0 B.N ??fun_for_add_4097_0
25 \ ??fun_for_add_4097_1:
26 \ 401C ADDS R0,R0,#+1
27 \ ??fun_for_add_4097_0:
28 \ B0F5805F CMP R0,#+4096
29 \ FBDD BLE.N ??fun_for_add_4097_1

这真是让我无比惊讶,在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汇编代码的一个奇怪现象的更多相关文章

  1. ARM Cortex M3(V7-M架构)硬件启动程序 一

    Cortex-m3启动代码分析笔记 启动代码文件名是STM32F10X.S,它的作用先总结下,然后再分析. 启动代码作用一般是: 1)堆和栈的初始化: 2)中断向量表定义: 3)地址重映射及中断向量表 ...

  2. STM32学习之路入门篇之指令集及cortex——m3的存储系统

    STM32学习之路入门篇之指令集及cortex——m3的存储系统 一.汇编语言基础 一).汇编语言:基本语法 1.汇编指令最典型的书写模式: 标号 操作码        操作数1, 操作数2,... ...

  3. cortex m0启动代码详解

    转自:http://www.cnblogs.com/mddblog/p/4920063.html 阅读目录 概述 1.堆栈空间定义 2.存放中断向量表 3. 复位中断函数(Reset_Handler) ...

  4. 浅析VS2010反汇编 VS 反汇编方法及常用汇编指令介绍 VS2015使用技巧 调试-反汇编 查看C语言代码对应的汇编代码

    浅析VS2010反汇编 2015年07月25日 21:53:11 阅读数:4374 第一篇 1. 如何进行反汇编 在调试的环境下,我们可以很方便地通过反汇编窗口查看程序生成的反汇编信息.如下图所示. ...

  5. STM8S汇编代码分析

    转载:http://blog.csdn.net/u010093140/article/details/50021897使用STVD建立完汇编工程项目之后(具本建立方法可以看我的另一篇博文http:// ...

  6. ARM汇编初探---汇编代码中都有哪几类指令---ARM伪指令介绍

    要学习一个东西首先要把概念搞清楚,以下仅仅是自己的一些关于汇编的理解. 可运行文件里的01码是机器码,机器码不等于汇编码,尽管机器码能够非常easy翻译成汇编码. 汇编码中包括非常多汇编指令.伪指令和 ...

  7. 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

    [嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )     一. 内存 ...

  8. 【freertos】002-posix模拟器设计与cortex m3异常处理

    目录 前言 posix 标准接口层设计 模拟器的系统心跳 模拟器的task底层实质 模拟器的任务切换原理 cortex M3/M4异常处理 双堆栈指针 双操作模式 栈帧 EXC_RETURN 前言 如 ...

  9. linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作

    一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...

随机推荐

  1. Schema-based AOP support

    本文参考至:spring-framework-reference.pdf的7.3 章节 [Schema-based AOP support] If you are unable to use Java ...

  2. Search Bar 去掉背景颜色

    storyboard里只能设置background颜色,可是发现clear color无法使用 其实代码还是可以设置的,那就是删除背景view [[self.searchBar.subviews ob ...

  3. Django Url编码问题

    Django Url编码问题   最近在学习Django,写一个blog程序练练手手.对于一个才开始接触web开发的来说,难免会遇到一些问题.   有一个这样的模板: {%for k,v in cat ...

  4. win7 64位系统调试zkemkeeper.dll出错误解决

    最近调用中控科技dll文件总是会出现上问题,网上找了大半天都没解决? 今天终于解决,原来是旧的dll文件是有问题,在中控网站上下载了最新的sdk(64位),解压,找到sdk的全部文件夹. 全选所有的: ...

  5. 【JQ成长笔记】jQuery cookie的使用

    jquery cookie挺好用的.简单实在.菜鸟都能用得上..额.文笔不好不好..咳咳.. 先来看看jq.cookie的aip 写入cookie $.cookie("this-cookie ...

  6. JS输出日历

    页面HTML代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> &l ...

  7. the C programming language 阅读笔记2

    1. 指针 1.1 自增符的使用 ++*p;//p指向的内容加一 (*p)++; //p指向的内容加一 *p++;//p本身自增 *++p; //p本身自增 因为诸如*和++这样的一元运算符在表达式求 ...

  8. J2SE知识点摘记(二十六)

    为了用“集合框架”的额外部分把排序支持添加到 Java 2 SDK,版本 1.2,核心 Java 库作了许多更改.像 String 和 Integer 类如今实现 Comparable 接口以提供自然 ...

  9. J2SE知识点摘记(十一)

    Thread t ↓ synchromized(this)                     线程t得到对象的锁标志 ↓                                   此时 ...

  10. Android 屏幕适配方式

    适配:即当前应用在相同的手机上面显示相同的效果.适配前需要首先确定当前手机所属像素密度类型(如:xhdpi.hdpi.mdpi等) 像素密度:每英寸上分布的像素点个数,单位(dpi,ppi),利用勾股 ...